diff options
| author | ruki <[email protected]> | 2026-01-04 22:34:55 +0800 |
|---|---|---|
| committer | GitHub <[email protected]> | 2026-01-04 22:34:55 +0800 |
| commit | 5631b98caa40ad06994f983a7c2a500be4b0e2b2 (patch) | |
| tree | 1c2df51151ec76c4ee5af6ad231523be62a583f6 | |
| parent | c24679f43a3fae814cf8e807a3756eb67b1d75dc (diff) | |
| parent | 8ff9ac175a2ee4e4a994b571e585f610c86de5c3 (diff) | |
Merge pull request #7178 from 24bit-xjkp/dev
refactor(rules/verilator): Switch from cmake to json format for Verilator build file parsing
| -rw-r--r-- | xmake/rules/verilator/verilator.lua | 218 |
1 files changed, 150 insertions, 68 deletions
diff --git a/xmake/rules/verilator/verilator.lua b/xmake/rules/verilator/verilator.lua index 2867d1e09..4631c5886 100644 --- a/xmake/rules/verilator/verilator.lua +++ b/xmake/rules/verilator/verilator.lua @@ -22,7 +22,9 @@ import("utils.progress") import("core.base.hashset") import("core.project.depend") -import("private.action.build.object", {alias = "build_objectfiles"}) +import("private.action.build.object", { alias = "build_objectfiles" }) +import("core.base.json") +import("lib.detect.find_tool") -- parse sourcefiles from cmakefile function _get_sourcefiles_from_cmake(target, cmakefile) @@ -33,7 +35,7 @@ function _get_sourcefiles_from_cmake(target, cmakefile) local supportfiles_fast = {} local targetname = target:name() local verilator_root = assert(target:data("verilator.root"), "no verilator_root!") - io.gsub(cmakefile, "set%((%S-) (.-)%)", function (key, values) + io.gsub(cmakefile, "set%((%S-) (.-)%)", function(key, values) if key == targetname .. "_GLOBAL" then -- get global class source files -- set(hello_GLOBAL "${VERILATOR_ROOT}/include/verilated.cpp" "${VERILATOR_ROOT}/include/verilated_threads.cpp") @@ -63,14 +65,31 @@ function _get_sourcefiles_from_cmake(target, cmakefile) end) -- get compiled source files - local sourcefiles = table.join(global_classes, classefiles_slow, classefiles_fast, supportfiles_slow, supportfiles_fast) + local sourcefiles = table.join(global_classes, classefiles_slow, classefiles_fast, supportfiles_slow, + supportfiles_fast) + return sourcefiles +end + +-- parse sourcefiles from jsonfile +function _get_sourcefiles_from_json(jsonfile) + local json_table = json.loadfile(jsonfile) + local sources_table = assert(json_table["sources"], "No sources field found in json file: %s.", jsonfile) + local global_classes = sources_table["global"] or {} + local classefiles_slow = sources_table["classes_slow"] or {} + local classefiles_fast = sources_table["classes_fast"] or {} + local supportfiles_slow = sources_table["support_slow"] or {} + local supportfiles_fast = sources_table["support_fast"] or {} + + -- get compiled source files + local sourcefiles = table.join(global_classes, classefiles_slow, classefiles_fast, supportfiles_slow, + supportfiles_fast) return sourcefiles end -- get languages -- -- Select the Verilog language generation to support in the compiler. --- This selects between v1364-1995, v1364-2001, v1364-2005, v1800-2005, v1800-2009, v1800-2012. +-- This selects between v1364-1995, v1364-2001, v1364-2005, v1800-2005, v1800-2009, v1800-2012, v1800-2017, v1800-2023. -- function _get_lanuage_flags(target) local language_v @@ -94,6 +113,7 @@ function _get_lanuage_flags(target) ["v1800-2009"] = "+1800-2009ext+v", ["v1800-2012"] = "+1800-2012ext+v", ["v1800-2017"] = "+1800-2017ext+v", + ["v1800-2023"] = "+1800-2023ext+v", } local flag = maps[language_v] if flag then @@ -104,16 +124,27 @@ function _get_lanuage_flags(target) end end +function _get_makefile_type(verilator) + local tool = assert(find_tool("verilator", { program = verilator, version = true }), "verilator not found!") + local version = tool.version + local support_json = version >= "5.036" + local makefile_type = support_json and "json" or "cmake" + return support_json, makefile_type +end + function config(target) - local toolchain = assert(target:toolchain("verilator"), 'we need to set_toolchains("verilator") in target("%s")', target:name()) + local toolchain = assert(target:toolchain("verilator"), 'we need to set_toolchains("verilator") in target("%s")', + target:name()) local verilator = assert(toolchain:config("verilator"), "verilator not found!") + local support_json, makefile_type = _get_makefile_type(verilator) local autogendir = path.join(target:autogendir(), "rules", "verilator") local tmpdir = os.tmpfile() .. ".dir" - local cmakefile = path.join(tmpdir, "test.cmake") + local makefile = path.join(tmpdir, "test." .. makefile_type) local sourcefile = path.join(tmpdir, "main.v") - local argv = {"--cc", "--make", "cmake", "--prefix", "test", "--Mdir", tmpdir, "main.v"} + local argv = { "--cc", "--make", makefile_type, "--prefix", "test", "--Mdir", tmpdir, "main.v" } local flags = target:values("verilator.flags") - local switches_flags = hashset.of( "sc", "coverage", "timing", "trace", "trace-fst", "threads") + local switches_flags = hashset.of("sc", "coverage", "timing", "trace", "trace-vcd", "trace-fst", "trace-saif", + "threads") if flags then for idx, flag in ipairs(flags) do if flag:startswith("--") and switches_flags:has(flag:sub(3)) then @@ -134,39 +165,55 @@ endmodule]]) os.mkdir(tmpdir) -- we just pass relative sourcefile path to solve this issue on windows. -- @see https://github.com/verilator/verilator/issues/3873 - os.runv(verilator, argv, {curdir = tmpdir, envs = toolchain:runenvs()}) + os.runv(verilator, argv, { curdir = tmpdir, envs = toolchain:runenvs() }) - -- parse some configurations from cmakefile + -- parse some configurations from makefile local verilator_root local switches = {} - local targetname = target:name() - io.gsub(cmakefile, "set%((%S-) (.-)%)", function (key, values) - if key == "VERILATOR_ROOT" then - verilator_root = values:match("\"(.-)\" CACHE PATH") - if not verilator_root then - verilator_root = values:match("(.-) CACHE PATH") + local options_table = {} + + if support_json then + local json_table = json.loadfile(makefile) + local system_table = assert(json_table["system"], "No system field found in json file: %s.", makefile) + verilator_root = system_table["verilator_root"] + options_table = assert(json_table["options"], "No options field found in json file: %s.", makefile) + switches.SC = options_table["system_c"] and "1" or "0" + switches.COVERAGE = options_table["coverage"] and "1" or "0" + switches.TIMING = options_table["use_timing"] and "1" or "0" + -- The thread field is a integer in float format, we need to convert it to string. + switches.THREADS = string.format("%d", options_table["threads"] or 1) + switches.TRACE_VCD = options_table["trace_vcd"] and "1" or "0" + switches.TRACE_FST = options_table["trace_fst"] and "1" or "0" + switches.TRACE_SAIF = options_table["trace_saif"] and "1" or "0" + else + io.gsub(makefile, "set%((%S-) (.-)%)", function(key, values) + if key == "VERILATOR_ROOT" then + verilator_root = values:match("\"(.-)\" CACHE PATH") + if not verilator_root then + verilator_root = values:match("(.-) CACHE PATH") + end + elseif key == "test_SC" then + -- SystemC output mode? 0/1 (from --sc) + switches.SC = values:trim() + elseif key == "test_COVERAGE" then + -- Coverage output mode? 0/1 (from --coverage) + switches.COVERAGE = values:trim() + elseif key == "test_TIMING" then + -- Timing mode? 0/1 (from --timing) + switches.TIMING = values:trim() + elseif key == "test_THREADS" then + -- Threaded output mode? 1/N threads (from --threads) + switches.THREADS = values:trim() + elseif key == "test_TRACE_VCD" then + -- VCD Tracing output mode? 0/1 (from --trace) + switches.TRACE_VCD = values:trim() + elseif key == "test_TRACE_FST" then + -- FST Tracing output mode? 0/1 (from --trace-fst) + switches.TRACE_FST = values:trim() end - elseif key == "test_SC" then - -- SystemC output mode? 0/1 (from --sc) - switches.SC = values:trim() - elseif key == "test_COVERAGE" then - -- Coverage output mode? 0/1 (from --coverage) - switches.COVERAGE = values:trim() - elseif key == "test_TIMING" then - -- Timing mode? 0/1 (from --timing) - switches.TIMING = values:trim() - elseif key == "test_THREADS" then - -- Threaded output mode? 1/N threads (from --threads) - switches.THREADS = values:trim() - elseif key == "test_TRACE_VCD" then - -- VCD Tracing output mode? 0/1 (from --trace) - switches.TRACE_VCD = values:trim() - elseif key == "test_TRACE_FST" then - -- FST Tracing output mode? 0/1 (from --trace-fst) - switches.TRACE_FST = values:trim() - end + end) + end - end) assert(verilator_root, "the verilator root directory not found!") target:data_set("verilator.root", verilator_root) @@ -174,49 +221,68 @@ endmodule]]) if not os.isfile(autogendir) then os.mkdir(autogendir) end - target:add("includedirs", autogendir, {public = true}) - target:add("includedirs", path.join(verilator_root, "include"), {public = true}) - target:add("includedirs", path.join(verilator_root, "include", "vltstd"), {public = true}) + target:add("includedirs", autogendir, { public = true }) + target:add("includedirs", path.join(verilator_root, "include"), { public = true }) + target:add("includedirs", path.join(verilator_root, "include", "vltstd"), { public = true }) -- set languages local languages = target:get("languages") local cxxlang = false for _, lang in ipairs(languages) do - if lang:startswith("xx") or lang:startswith("++") then + if lang:find("xx", 1, true) or lang:find("++", 1, true) then cxxlang = true break end end if not cxxlang then - target:add("languages", "c++20", {public = true}) + target:add("languages", "c++20", { public = true }) end -- add definitions for switches for k, v in table.orderpairs(switches) do - target:add("defines", "VM_" .. k .. "=" .. v, {public = true}) + target:add("defines", "VM_" .. k .. "=" .. v, { public = true }) + end + if support_json then + local cflags = options_table["cflags"] or {} + for _, flag in ipairs(cflags) do + if flag:startswith("-D") then + target:add("defines", flag:sub(3), { public = true }) + end + end end -- add syslinks - if target:is_plat("linux", "macosx") and switches.THREADS == "1" then - target:add("syslinks", "pthread") - end - if target:is_plat("linux", "macosx") and switches.TRACE_FST == "1" then - target:add("syslinks", "z") + if support_json then + local ldflags = options_table["ldflags"] or {} + for _, flag in ipairs(ldflags) do + if flag:startswith("-l") then + target:add("syslinks", flag:sub(3)) + end + end + else + if target:is_plat("linux", "macosx") and switches.THREADS == "1" then + target:add("syslinks", "pthread") + end + if target:is_plat("linux", "macosx") and switches.TRACE_FST == "1" then + target:add("syslinks", "z") + end end os.rm(tmpdir) end function build_cppfiles(target, jobgraph, sourcebatch, opt) - local toolchain = assert(target:toolchain("verilator"), 'we need to set_toolchains("verilator") in target("%s")', target:name()) + local toolchain = assert(target:toolchain("verilator"), 'we need to set_toolchains("verilator") in target("%s")', + target:name()) local verilator = assert(toolchain:config("verilator"), "verilator not found!") + local support_json, makefile_type = _get_makefile_type(verilator) local autogendir = path.join(target:autogendir(), "rules", "verilator") local targetname = target:name() - local cmakefile = path.join(autogendir, targetname .. ".cmake") + local makefile = path.join(autogendir, targetname .. "." .. makefile_type) -- build verilog files depend.on_changed(function() - local argv = {"--cc", "--make", "cmake", "--prefix", targetname, "--Mdir", autogendir} + local argv = { "--cc", "--make", makefile_type, "--prefix", targetname, "--Mdir", autogendir } local flags = target:values("verilator.flags") if flags then table.join2(argv, flags) @@ -237,15 +303,21 @@ function build_cppfiles(target, jobgraph, sourcebatch, opt) end -- generate c++ sourcefiles - os.vrunv(verilator, argv, {envs = toolchain:runenvs()}) - - end, {dependfile = cmakefile .. ".d", - files = sourcebatch.sourcefiles, - changed = target:is_rebuilt(), - lastmtime = os.mtime(cmakefile)}) + os.vrunv(verilator, argv, { envs = toolchain:runenvs() }) + end, { + dependfile = makefile .. ".d", + files = sourcebatch.sourcefiles, + changed = target:is_rebuilt(), + lastmtime = os.mtime(makefile) + }) -- get compiled source files - local sourcefiles = _get_sourcefiles_from_cmake(target, cmakefile) + local sourcefiles + if support_json then + sourcefiles = _get_sourcefiles_from_json(makefile) + else + sourcefiles = _get_sourcefiles_from_cmake(target, makefile) + end -- do build local sourcebatch_cpp = { @@ -253,7 +325,8 @@ function build_cppfiles(target, jobgraph, sourcebatch, opt) sourcekind = "cxx", sourcefiles = sourcefiles, objectfiles = {}, - dependfiles = {}} + dependfiles = {} + } for _, sourcefile in ipairs(sourcefiles) do local objectfile = target:objectfile(sourcefile) local dependfile = target:dependfile(objectfile) @@ -265,14 +338,16 @@ function build_cppfiles(target, jobgraph, sourcebatch, opt) end function buildcmd_vfiles(target, batchcmds, sourcebatch, opt) - local toolchain = assert(target:toolchain("verilator"), 'we need to set_toolchains("verilator") in target("%s")', target:name()) + local toolchain = assert(target:toolchain("verilator"), 'we need to set_toolchains("verilator") in target("%s")', + target:name()) local verilator = assert(toolchain:config("verilator"), "verilator not found!") + local _, makefile_type = _get_makefile_type(verilator) local autogendir = path.join(target:autogendir(), "rules", "verilator") local targetname = target:name() - local cmakefile = path.join(autogendir, targetname .. ".cmake") - local dependfile = cmakefile .. ".d" + local makefile = path.join(autogendir, targetname .. "." .. makefile_type) + local dependfile = makefile .. ".d" - local argv = {"--cc", "--make", "cmake", "--prefix", targetname, "--Mdir", path(autogendir)} + local argv = { "--cc", "--make", makefile_type, "--prefix", targetname, "--Mdir", path(autogendir) } local flags = target:values("verilator.flags") if flags then table.join2(argv, flags) @@ -284,7 +359,7 @@ function buildcmd_vfiles(target, batchcmds, sourcebatch, opt) local sourcefiles = sourcebatch.sourcefiles for _, sourcefile in ipairs(sourcefiles) do batchcmds:show_progress(opt.progress, "${color.build.object}compiling.verilog %s", sourcefile) - table.insert(argv, path(sourcefile, function (v) + table.insert(argv, path(sourcefile, function(v) -- we need to use slashes to fix it on windows -- @see https://github.com/verilator/verilator/issues/3873 if is_host("windows") then @@ -295,22 +370,29 @@ function buildcmd_vfiles(target, batchcmds, sourcebatch, opt) end -- generate c++ sourcefiles - batchcmds:vrunv(verilator, argv, {envs = toolchain:runenvs()}) + batchcmds:vrunv(verilator, argv, { envs = toolchain:runenvs() }) batchcmds:add_depfiles(sourcefiles) - batchcmds:set_depmtime(os.mtime(cmakefile)) + batchcmds:set_depmtime(os.mtime(makefile)) batchcmds:set_depcache(dependfile) end function buildcmd_cppfiles(target, batchcmds, sourcebatch, opt) - local toolchain = assert(target:toolchain("verilator"), 'we need set_toolchains("verilator") in target("%s")', target:name()) + local toolchain = assert(target:toolchain("verilator"), 'we need set_toolchains("verilator") in target("%s")', + target:name()) local verilator = assert(toolchain:config("verilator"), "verilator not found!") + local support_json, makefile_type = _get_makefile_type(verilator) local autogendir = path.join(target:autogendir(), "rules", "verilator") local targetname = target:name() - local cmakefile = path.join(autogendir, targetname .. ".cmake") + local makefile = path.join(autogendir, targetname .. "." .. makefile_type) local dependfile = path.join(autogendir, targetname .. ".build.d") -- get compiled source files - local sourcefiles = _get_sourcefiles_from_cmake(target, cmakefile) + local sourcefiles + if support_json then + sourcefiles = _get_sourcefiles_from_json(makefile) + else + sourcefiles = _get_sourcefiles_from_cmake(target, makefile) + end -- do build for _, sourcefile in ipairs(sourcefiles) do |
