From 20957e5ba75f929ca50e9042e5d61178a999bd3d Mon Sep 17 00:00:00 2001 From: Fischer Moseley <42497969+fischermoseley@users.noreply.github.com> Date: Sat, 8 Apr 2023 14:07:08 -0400 Subject: [PATCH] refactor to separate verilog and python --- .github/workflows/build_icestick_examples.yml | 26 +- .github/workflows/build_nexys_a7_examples.yml | 14 +- src/manta/__init__.py | 540 ++++++++---------- src/manta/io_core_def_tmpl.v | 51 ++ src/manta/io_core_inst_tmpl.v | 20 + src/manta/logic_analyzer_inst_tmpl.v | 16 + ...lyzer_template.v => logic_analyzer_tmpl.v} | 0 src/manta/lut_ram_inst_tmpl.v | 14 + ...ample_mem_template.v => sample_mem_tmpl.v} | 0 src/manta/trigger_block_inst_tmpl.v | 12 + src/manta/trigger_block_template.v | 12 +- src/manta/uart_rx_bridge_rx_inst_templ.v | 19 + src/manta/uart_tx_bridge_tx_inst_templ.v | 23 + 13 files changed, 412 insertions(+), 335 deletions(-) create mode 100644 src/manta/io_core_def_tmpl.v create mode 100644 src/manta/io_core_inst_tmpl.v create mode 100644 src/manta/logic_analyzer_inst_tmpl.v rename src/manta/{logic_analyzer_template.v => logic_analyzer_tmpl.v} (100%) create mode 100644 src/manta/lut_ram_inst_tmpl.v rename src/manta/{sample_mem_template.v => sample_mem_tmpl.v} (100%) create mode 100644 src/manta/trigger_block_inst_tmpl.v create mode 100644 src/manta/uart_rx_bridge_rx_inst_templ.v create mode 100644 src/manta/uart_tx_bridge_tx_inst_templ.v diff --git a/.github/workflows/build_icestick_examples.yml b/.github/workflows/build_icestick_examples.yml index e403bdc..c5ff0d3 100644 --- a/.github/workflows/build_icestick_examples.yml +++ b/.github/workflows/build_icestick_examples.yml @@ -5,10 +5,28 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - name: install yosys + + - name: Install Manta from Source + run: | + pip install setuptools --upgrade + + # omitting the following commmand causes the version of setuptools + # used by python to get confused, and it doesn't detect the name + # or version of the package from pyproject.toml - so the following + # workaround is used: + # https://github.com/pypa/setuptools/issues/3269#issuecomment-1254507377 + export DEB_PYTHON_INSTALL_LAYOUT=deb_system + + python3 -m pip install . + + - name: Install Icestorm Tools run: | wget --no-verbose https://github.com/YosysHQ/oss-cad-suite-build/releases/download/2023-02-23/oss-cad-suite-linux-x64-20230223.tgz - tar -xzf oss-cad-suite-linux-x64-20230223.tgz + tar -xzf oss-cad-suite-linux-x64-20230223.tgz export PATH="$(pwd)/oss-cad-suite/bin/:$PATH" - cd examples/icestick/counter - ./build.sh \ No newline at end of file + + - name: Build io_core + run: make icestick_io_core + + - name: Build lut_ram + run: make icestick_lut_ram \ No newline at end of file diff --git a/.github/workflows/build_nexys_a7_examples.yml b/.github/workflows/build_nexys_a7_examples.yml index a589520..72f4cfa 100644 --- a/.github/workflows/build_nexys_a7_examples.yml +++ b/.github/workflows/build_nexys_a7_examples.yml @@ -26,13 +26,11 @@ jobs: python3 -m pip install . - - name: Generate Core - run: manta gen examples/nexys_a7/counter/manta.yaml examples/nexys_a7/counter/src/debug.sv + - name: Build io_core + run: make nexys_a7_io_core - - name: Build Verilog - working-directory: examples/nexys_a7/counter - run: mkdir obj && python3 lab-bc.py + - name: Build logic_analyzer + run: make nexys_a7_logic_analyzer - - name: Print build.log - working-directory: examples/nexys_a7/counter - run: cat obj/build.log \ No newline at end of file + - name: Build lut_ram + run: make nexys_a7_lut_ram \ No newline at end of file diff --git a/src/manta/__init__.py b/src/manta/__init__.py index a51440c..f5b5edf 100644 --- a/src/manta/__init__.py +++ b/src/manta/__init__.py @@ -5,6 +5,94 @@ from datetime import datetime version = "0.0.0" +class VerilogManipulator: + def __init__(self, filepath=None): + if filepath is not None: + self.hdl = pkgutil.get_data(__name__, filepath).decode() + + else: + self.hdl = None + + def sub(self, replace, find): + # sometimes we have integer inputs, want to accomodate + if isinstance(replace, str): + replace_str = replace + + elif isinstance(replace, int): + replace_str = str(replace) + + else: + raise ValueError("Only string and integer arguments supported.") + + + # if the string being subbed in isn't multiline, just + # find-and-replace like normal: + if "\n" not in replace_str: + self.hdl = self.hdl.replace(find, replace_str) + + # if the string being substituted in is multiline, + # make sure the replace text gets put at the same + # indentation level by adding whitespace to left + # of the line. + else: + for line in self.hdl.split("\n"): + if find in line: + # get whitespace that's on the left side of the line + whitespace = line.rstrip().replace(line.lstrip(), "") + + # add it to every line, except the first + replace_as_lines = replace_str.split("\n") + replace_with_whitespace = f"\n{whitespace}".join(replace_as_lines) + + # replace the first occurance in the HDL with it + self.hdl = self.hdl.replace(find, replace_with_whitespace, 1) + + def get_hdl(self): + return self.hdl + + def net_dec(self, nets, net_type, trailing_comma = False): + """Takes a dictonary of nets in the format {probe: width}, and generates + the net declarations that would go in a Verilog module definition. + + For example, calling net_dec({foo : 1, bar : 4}, "input wire") would produce: + + input wire foo, + input [3:0] wire bar + + Which you'd then slap into your module declaration, along with all the other + inputs and outputs the module needs.""" + + dec = [] + for name, width in nets.items(): + if width == 1: + dec.append(f"{net_type} {name}") + + else: + dec.append(f"{net_type} [{width-1}:0] {name}") + + dec = ",\n".join(dec) + dec = dec + "," if trailing_comma else dec + return dec + + def net_conn(self, nets, trailing_comma = False): + """Takes a dictionary of nets in the format {probe: width}, and generates + the net connections that would go in the Verilog module instantiation. + + For example, calling net_conn({foo: 1, bar: 4}) would produce: + + .foo(foo), + .bar(bar) + + Which you'd then slap into your module instantiation, along with all the other + module inputs and outputs that get connected elsewhere.""" + + + conn = [f".{name}({name})" for name in nets] + conn = ",\n".join(conn) + conn = conn + "," if trailing_comma else conn + + return conn + class UARTInterface: def __init__(self, config): # Obtain port. Try to automatically detect port if "auto" is specified @@ -112,63 +200,24 @@ class UARTInterface: return ["input wire rx", "output reg tx"] def rx_hdl_def(self): - uart_rx_def = pkgutil.get_data(__name__, "rx_uart.v").decode() - bridge_rx_def = pkgutil.get_data(__name__, "bridge_rx.v").decode() + uart_rx_def = VerilogManipulator("rx_uart.v").get_hdl() + bridge_rx_def = VerilogManipulator("bridge_rx.v").get_hdl() return uart_rx_def + '\n' + bridge_rx_def def tx_hdl_def(self): - uart_tx_def = pkgutil.get_data(__name__, "uart_tx.v").decode() - bridge_tx_def = pkgutil.get_data(__name__, "bridge_tx.v").decode() + uart_tx_def = VerilogManipulator("uart_tx.v").get_hdl() + bridge_tx_def = VerilogManipulator("bridge_tx.v").get_hdl() return bridge_tx_def + '\n' + uart_tx_def def rx_hdl_inst(self): - return f""" - rx_uart #(.CLOCKS_PER_BAUD({self.clocks_per_baud})) urx ( - .i_clk(clk), - .i_uart_rx(rx), - .o_wr(urx_brx_axiv), - .o_data(urx_brx_axid)); - - logic [7:0] urx_brx_axid; - logic urx_brx_axiv; - - bridge_rx brx ( - .clk(clk), - - .rx_data(urx_brx_axid), - .rx_valid(urx_brx_axiv), - - .addr_o(), - .wdata_o(), - .rw_o(), - .valid_o()); - """ + rx = VerilogManipulator("uart_rx_bridge_rx_inst_templ.v") + rx.sub(self.clocks_per_baud, "/* CLOCKS_PER_BAUD */") + return rx.get_hdl() def tx_hdl_inst(self): - return f""" - bridge_tx btx ( - .clk(clk), - - .rdata_i(), - .rw_i(), - .valid_i(), - - .ready_i(utx_btx_ready), - .data_o(btx_utx_data), - .valid_o(btx_utx_valid)); - - logic utx_btx_ready; - logic btx_utx_valid; - logic [7:0] btx_utx_data; - - uart_tx #(.CLOCKS_PER_BAUD({self.clocks_per_baud})) utx ( - .clk(clk), - - .data(btx_utx_data), - .valid(btx_utx_valid), - .ready(utx_btx_ready), - - .tx(tx));\n""" + tx = VerilogManipulator("uart_tx_bridge_tx_inst_templ.v") + tx.sub(self.clocks_per_baud, "/* CLOCKS_PER_BAUD */") + return tx.get_hdl() class IOCoreProbe: def __init__(self, name, width, direction, base_addr, interface): @@ -241,120 +290,62 @@ class IOCore: def hdl_inst(self): - # TODO: make this a string comprehension - inst_ports = [f".{probe.name}({probe.name}),\n " for probe in self.probes] - inst_ports = "".join(inst_ports) - inst_ports = inst_ports.rstrip() + inst = VerilogManipulator("io_core_inst_tmpl.v") + inst.sub(self.name, "/* MODULE_NAME */") + inst.sub(self.name + "_inst", "/* INST_NAME */") - return f""" -{self.name} {self.name}_inst( - .clk(clk), + probes = {probe.name:probe.width for probe in self.probes} - // ports - {inst_ports} + inst_ports = inst.net_conn(probes, trailing_comma=True) + inst.sub(inst_ports, "/* INST_PORTS */") - // input port - .addr_i(), - .wdata_i(), - .rdata_i(), - .rw_i(), - .valid_i(), + return inst.get_hdl() - // output port - .addr_o(), - .wdata_o(), - .rdata_o(), - .rw_o(), - .valid_o() - ); -""" def hdl_def(self): + io_core = VerilogManipulator("io_core_def_tmpl.v") + io_core.sub(self.name, "/* MODULE_NAME */") + io_core.sub(self.max_addr, "/* MAX_ADDR */") + # generate declaration - top_level_ports = ',\n '.join(self.hdl_top_level_ports()) + top_level_ports = ',\n'.join(self.hdl_top_level_ports()) top_level_ports += ',' - declaration = f""" -module {self.name} ( - input wire clk, - - // ports - {top_level_ports} - - // input port - input wire [15:0] addr_i, - input wire [15:0] wdata_i, - input wire [15:0] rdata_i, - input wire rw_i, - input wire valid_i, - - // output port - output reg [15:0] addr_o, - output reg [15:0] wdata_o, - output reg [15:0] rdata_o, - output reg rw_o, - output reg valid_o - ); -""" + io_core.sub(top_level_ports, "/* TOP_LEVEL_PORTS */") # generate memory handling - read_case_statement_body = "" - write_case_statement_body = "" + rcsb = "" # read case statement body + wcsb = "" # write case statement body for probe in self.probes: # add to read block if probe.width == 16: - read_case_statement_body += f"\t\t\t\t\t{probe.base_addr}: rdata_o <= {probe.name};\n" + rcsb += f"{probe.base_addr}: rdata_o <= {probe.name};\n" else: - read_case_statement_body += f"\t\t\t\t\t{probe.base_addr}: rdata_o <= {{{16-probe.width}'b0, {probe.name}}};\n" + rcsb += f"{probe.base_addr}: rdata_o <= {{{16-probe.width}'b0, {probe.name}}};\n" # if output, add to write block if probe.direction == "output": if probe.width == 1: - write_case_statement_body += f"\t\t\t\t\t{probe.base_addr}: {probe.name} <= wdata_i[0];\n" + wcsb += f"{probe.base_addr}: {probe.name} <= wdata_i[0];\n" elif probe.width == 16: - write_case_statement_body += f"\t\t\t\t\t{probe.base_addr}: {probe.name} <= wdata_i;\n" + wcsb += f"{probe.base_addr}: {probe.name} <= wdata_i;\n" else: - write_case_statement_body += f"\t\t\t\t\t{probe.base_addr}: {probe.name} <= wdata_i[{probe.width-1}:0];\n" + wcsb += f"{probe.base_addr}: {probe.name} <= wdata_i[{probe.width-1}:0];\n" # remove trailing newline - read_case_statement_body = read_case_statement_body.rstrip() - write_case_statement_body = write_case_statement_body.rstrip() + rcsb = rcsb.rstrip() + wcsb = wcsb.rstrip() - memory_handler_hdl = f""" -parameter BASE_ADDR = 0; -always @(posedge clk) begin - addr_o <= addr_i; - wdata_o <= wdata_i; - rdata_o <= rdata_i; - rw_o <= rw_i; - valid_o <= valid_i; - rdata_o <= rdata_i; + io_core.sub(rcsb, "/* READ_CASE_STATEMENT_BODY */") + io_core.sub(wcsb, "/* WRITE_CASE_STATEMENT_BODY */") + + return io_core.get_hdl() - // check if address is valid - if( (valid_i) && (addr_i >= BASE_ADDR) && (addr_i <= BASE_ADDR + {self.max_addr})) begin - - if(!rw_i) begin // reads - case (addr_i) -{read_case_statement_body} - endcase - end - - else begin // writes - case (addr_i) -{write_case_statement_body} - endcase - end - end - end -""" - - hdl = declaration + memory_handler_hdl + "endmodule" - return hdl def hdl_top_level_ports(self): ports = [] @@ -379,31 +370,17 @@ class LUTRAMCore: self.max_addr = self.base_addr + self.size - 1 def hdl_inst(self): - hdl = f""" - lut_ram #(.DEPTH({self.size})) {self.name} ( - .clk(clk), - - .addr_i(), - .wdata_i(), - .rdata_i(), - .rw_i(), - .valid_i(), - - .addr_o(), - .wdata_o(), - .rdata_o(), - .rw_o(), - .valid_o());\n""" - - return hdl + inst = VerilogManipulator("lut_ram_inst_tmpl.v") + inst.sub(self.size, "/* DEPTH */") + inst.sub(self.name, "/* INST_NAME */") + return inst.get_hdl() def hdl_def(self): - hdl = pkgutil.get_data(__name__, "lut_ram.v").decode() - return hdl + return VerilogManipulator("lut_ram.v").get_hdl() def hdl_top_level_ports(self): # no top_level connections since this core just lives on the bus - return [] + return "" def read(self, addr): return self.interface.read_register(addr + self.base_addr) @@ -448,83 +425,59 @@ class LogicAnalyzerCore: self.max_addr = self.sample_mem_base_addr + self.sample_depth def hdl_inst(self): - ports = [] + la_inst = VerilogManipulator("logic_analyzer_inst_tmpl.v") - ports = [f".{name}({name})," for name in self.probes.keys()] - ports = "\n\t\t".join(ports) + # add module name to instantiation + la_inst.sub(self.name, "/* INST_NAME */") - hdl = f""" - logic_analyzer {self.name} ( - .clk(clk), + # add net connections to instantiation + conns = la_inst.net_conn(self.probes, trailing_comma=True) + la_inst.sub(conns, "/* NET_CONNS */") + return la_inst.get_hdl() - .addr_i(), - .wdata_i(), - .rdata_i(), - .rw_i(), - .valid_i(), - - {ports} - - .addr_o(), - .wdata_o(), - .rdata_o(), - .rw_o(), - .valid_o());\n""" - - return hdl - - def generate_trigger_block(self): - trigger_block_hdl = pkgutil.get_data(__name__, "trigger_block_template.v").decode() + def gen_trigger_block_def(self): + trigger_block = VerilogManipulator("trigger_block_template.v") # add probe ports to module declaration - # - these are the ports that belong to the logic analyzer, but - # need to be included in the trigger_block module declaration - probe_ports = [] - for name, width in self.probes.items(): - if width == 1: - probe_ports.append(f"input wire {name}") - else: - probe_ports.append(f"input wire [{width-1}:0] {name}") + # these ports belong to the logic analyzer, but + # need to be included in the trigger_block module declaration + probe_ports = trigger_block.net_dec(self.probes, "input wire", trailing_comma=True) + trigger_block.sub(probe_ports, "/* PROBE_PORTS */") - probe_ports = ",\n\t".join(probe_ports) - probe_ports = probe_ports + "," - trigger_block_hdl = trigger_block_hdl.replace("// @PROBE_PORTS", probe_ports) # add trigger cores to module definition - # - these are instances of the trigger module, of which one gets wired - # into each probe + # these are instances of the trigger module, of which one gets wired + # into each probe trigger_module_insts = [] for name, width in self.probes.items(): - trigger_module_inst = f"reg [3:0] {name}_trigger_op = 0;\n\t" + trig_inst = VerilogManipulator("trigger_block_inst_tmpl.v") + trig_inst.sub(width, "/* INPUT_WIDTH */") + trig_inst.sub(f"{name}_trigger", "/* NAME */") + + trig_inst.sub(f"reg [3:0] {name}_op = 0;", "/* OP_DEC */") + trig_inst.sub(f"reg {name}_trig;", "/* TRIG_DEC */") if width == 1: - trigger_module_inst += f"reg {name}_trigger_arg = 0;\n\t" + trig_inst.sub(f"reg {name}_arg = 0;", "/* ARG_DEC */") else: - trigger_module_inst += f"reg [{width-1}:0] {name}_trigger_arg = 0;\n\t" + trig_inst.sub(f"reg [{width-1}:0] {name}_arg = 0;", "/* ARG_DEC */") - trigger_module_inst += f"reg {name}_trig;\n\t" - trigger_module_inst += f""" - trigger #(.INPUT_WIDTH({width})) {name}_trigger ( - .clk(clk), + trig_inst.sub(name, "/* PROBE */") + trig_inst.sub(f"{name}_op", "/* OP */") + trig_inst.sub(f"{name}_arg", "/* ARG */") + trig_inst.sub(f"{name}_trig", "/* TRIG */") - .probe({name}), - .op({name}_trigger_op), - .arg({name}_trigger_arg), - .trig({name}_trig) - ); - """ - trigger_module_insts.append(trigger_module_inst) + trigger_module_insts.append(trig_inst.get_hdl()) - trigger_module_insts = "".join(trigger_module_insts) - trigger_module_insts = trigger_module_insts.rstrip() - trigger_block_hdl = trigger_block_hdl.replace("// @TRIGGER_MODULE_INSTS", trigger_module_insts) + trigger_module_insts = "\n".join(trigger_module_insts) + trigger_block.sub(trigger_module_insts, "/* TRIGGER_MODULE_INSTS */") # add combined individual triggers - combined_individual_triggers = [f"{name}_trig" for name in self.probes] - combined_individual_triggers = " || ".join(combined_individual_triggers) - combined_individual_triggers = f"assign trig = {combined_individual_triggers};" - trigger_block_hdl = trigger_block_hdl.replace("// @COMBINE_INDIV_TRIGGERS", combined_individual_triggers) + cit = [f"{name}_trig" for name in self.probes] + cit = " || ".join(cit) + cit = f"assign trig = {cit};" + trigger_block.sub(cit, " /* COMBINE_INDIV_TRIGGERS */") # add read and write block case statement bodies rcsb = "" # read case statement body @@ -532,38 +485,30 @@ class LogicAnalyzerCore: addr = 0 for i, name in enumerate(self.probes): addr = 2 * i - rcsb += f"\t\t\t\t\tBASE_ADDR + {addr}: rdata_o <= {name}_trigger_op;\n" - wcsb += f"\t\t\t\t\tBASE_ADDR + {addr}: {name}_trigger_op <= wdata_i;\n" + rcsb += f"BASE_ADDR + {addr}: rdata_o <= {name}_op;\n" + wcsb += f"BASE_ADDR + {addr}: {name}_op <= wdata_i;\n" addr = (2 * i) + 1 - rcsb += f"\t\t\t\t\tBASE_ADDR + {addr}: rdata_o <= {name}_trigger_arg;\n" - wcsb += f"\t\t\t\t\tBASE_ADDR + {addr}: {name}_trigger_arg <= wdata_i;\n" + rcsb += f"BASE_ADDR + {addr}: rdata_o <= {name}_arg;\n" + wcsb += f"BASE_ADDR + {addr}: {name}_arg <= wdata_i;\n" rcsb = rcsb.strip() wcsb = wcsb.strip() - trigger_block_hdl = trigger_block_hdl.replace("// @READ_CASE_STATEMENT_BODY", rcsb) - trigger_block_hdl = trigger_block_hdl.replace("// @WRITE_CASE_STATEMENT_BODY", wcsb) - trigger_block_hdl = trigger_block_hdl.replace("// @MAX_ADDR", str(addr)) - return trigger_block_hdl + trigger_block.sub(rcsb, "/* READ_CASE_STATEMENT_BODY */") + trigger_block.sub(wcsb, "/* WRITE_CASE_STATEMENT_BODY */") + trigger_block.sub(addr, "/* MAX_ADDR */") - def generate_sample_mem(self): - sample_mem_hdl = pkgutil.get_data(__name__, "sample_mem_template.v").decode() + return trigger_block.get_hdl() + + def gen_sample_mem_def(self): + sample_mem = VerilogManipulator("sample_mem_tmpl.v") # add probe ports to module declaration # - these are the ports that belong to the logic analyzer, but # need to be included in the trigger_block module declaration - probe_ports = [] - for name, width in self.probes.items(): - if width == 1: - probe_ports.append(f"input wire {name}") - else: - probe_ports.append(f"input wire [{width-1}:0] {name}") - - probe_ports = ",\n\t".join(probe_ports) - probe_ports = probe_ports + "," - sample_mem_hdl = sample_mem_hdl.replace("/* PROBE_PORTS */", probe_ports) - + probe_ports = sample_mem.net_dec(self.probes, "input wire", trailing_comma=True) + sample_mem.sub(probe_ports, "/* PROBE_PORTS */") # concatenate probes to BRAM input total_probe_width = sum([width for name, width in self.probes.items()]) @@ -576,76 +521,54 @@ class LogicAnalyzerCore: concat = ", ".join([name for name in self.probes]) concat = f"{{{zero_pad_width}'b0, {concat}}}" - sample_mem_hdl = sample_mem_hdl.replace("/* CONCAT */", concat) + sample_mem.sub(concat, "/* CONCAT */") + return sample_mem.get_hdl() - return sample_mem_hdl - - def generate_logic_analyzer(self): - logic_analyzer_hdl = pkgutil.get_data(__name__, "logic_analyzer_template.v").decode() + def gen_logic_analyzer_def(self): + la = VerilogManipulator("logic_analyzer_tmpl.v") # add top level probe ports to module declaration - # - these are the ports that belong to the logic analyzer, but - # need to be included in the trigger_block module declaration - - tlpp = [] # top level probe ports - for name, width in self.probes.items(): - if width == 1: - tlpp.append(f"input wire {name}") - else: - tlpp.append(f"input wire [{width-1}:0] {name}") - - tlpp = ",\n\t".join(tlpp) - tlpp = tlpp + "," - logic_analyzer_hdl = logic_analyzer_hdl.replace("/* TOP_LEVEL_PROBE_PORTS */", tlpp) + ports = la.net_dec(self.probes, "input wire", trailing_comma=True) + la.sub(ports, "/* TOP_LEVEL_PROBE_PORTS */") # assign base addresses to the FSM, trigger block, and sample mem - logic_analyzer_hdl = logic_analyzer_hdl.replace("/* FSM_BASE_ADDR */", str(self.fsm_base_addr)) - logic_analyzer_hdl = logic_analyzer_hdl.replace("/* TRIGGER_BLOCK_BASE_ADDR */", str(self.trigger_block_base_addr)) - logic_analyzer_hdl = logic_analyzer_hdl.replace("/* SAMPLE_MEM_BASE_ADDR */", str(self.sample_mem_base_addr)) + la.sub(self.fsm_base_addr, "/* FSM_BASE_ADDR */") + la.sub(self.trigger_block_base_addr, "/* TRIGGER_BLOCK_BASE_ADDR */") + la.sub(self.sample_mem_base_addr, "/* SAMPLE_MEM_BASE_ADDR */") # set sample depth - logic_analyzer_hdl = logic_analyzer_hdl.replace("/* SAMPLE_DEPTH */", str(self.sample_depth)) + la.sub(self.sample_depth, "/* SAMPLE_DEPTH */") # set probe ports for the trigger block and sample mem - pp = [] # probe ports - pp = [f".{name}({name})" for name in self.probes] - pp = ",\n\t\t".join(pp) - pp = pp + "," - logic_analyzer_hdl = logic_analyzer_hdl.replace("/* TRIGGER_BLOCK_PROBE_PORTS */", pp) - logic_analyzer_hdl = logic_analyzer_hdl.replace("/* SAMPLE_MEM_PROBE_PORTS */", pp) - - return logic_analyzer_hdl + probe_ports = la.net_conn(self.probes, trailing_comma=True) + la.sub(probe_ports, "/* TRIGGER_BLOCK_PROBE_PORTS */") + la.sub(probe_ports, "/* SAMPLE_MEM_PROBE_PORTS */") + return la.get_hdl() def hdl_def(self): # Return an autogenerated verilog module definition for the core. - # load source files= - la_fsm_hdl = pkgutil.get_data(__name__, "la_fsm.v").decode() - dual_port_bram_hdl = pkgutil.get_data(__name__, "dual_port_bram.v").decode() - trigger_hdl = pkgutil.get_data(__name__, "trigger.v").decode() + # load source files + la_fsm = VerilogManipulator("la_fsm.v").get_hdl() + dual_port_bram = VerilogManipulator("dual_port_bram.v").get_hdl() + trigger = VerilogManipulator("trigger.v").get_hdl() + trigger_block = self.gen_trigger_block_def() + sample_mem = self.gen_sample_mem_def() + logic_analyzer = self.gen_logic_analyzer_def() - # generate trigger block - trigger_block_hdl = self.generate_trigger_block() - - # generate sample memory - sample_mem_hdl = self.generate_sample_mem() - - # generate logic analyzer - logic_analyzer_hdl = self.generate_logic_analyzer() - - return logic_analyzer_hdl + la_fsm_hdl + sample_mem_hdl + dual_port_bram_hdl + trigger_block_hdl + trigger_hdl + return logic_analyzer + la_fsm + sample_mem + dual_port_bram + trigger_block + trigger def hdl_top_level_ports(self): - # this should return the probes that we want to connect to top-level, but as a list of verilog ports - + # the probes that we want as ports on the top-level manta module ports = [] for name, width in self.probes.items(): if width == 1: ports.append(f"input wire {name}") + else: ports.append(f"input wire [{width-1}:0] {name}") - return ports + #return VerilogManipulator().net_dec(self.probes, "input wire") def run(self): pass @@ -780,7 +703,7 @@ class Manta: return config - def generate_connections(self): + def gen_connections(self): # generates hdl for registers that connect two modules together # make pairwise cores @@ -800,7 +723,7 @@ class Manta: return conns - def generate_instances(self): + def gen_instances(self): # generates hdl for modules that need to be connected together insts = [] @@ -841,9 +764,9 @@ class Manta: return insts - def generate_core_chain(self): - insts = self.generate_instances() - conns = self.generate_connections() + def gen_core_chain(self): + insts = self.gen_instances() + conns = self.gen_connections() core_chain = [] for i, inst in enumerate(insts): core_chain.append(inst) @@ -853,7 +776,7 @@ class Manta: return '\n'.join(core_chain) - def generate_header(self): + def gen_header(self): # generate header user = os.environ.get("USER", os.environ.get("USERNAME")) timestamp = datetime.now().strftime("%d %b %Y at %H:%M:%S") @@ -869,7 +792,7 @@ Provided under a GNU GPLv3 license. Go wild. """ return header - def generate_ex_inst(self): + def gen_example_inst(self): # this is a C-style block comment that contains an instantiation # of the configured manta instance - the idea is that a user # can copy-paste that into their design instead of trying to spot @@ -918,7 +841,7 @@ manta manta_inst ( */ """ - def generate_declaration(self): + def gen_declaration(self): # get all the top level connections for each module. interface_ports = self.interface.hdl_top_level_ports() @@ -947,7 +870,7 @@ module manta ( {ports}); """ - def generate_interface_rx(self): + def gen_interface_rx(self): # instantiate interface_rx, substitute in register names interface_rx_inst = self.interface.rx_hdl_inst() @@ -965,7 +888,7 @@ module manta ( return interface_rx_inst + interface_rx_conn - def generate_interface_tx(self): + def gen_interface_tx(self): # connect core_chain to interface_tx interface_tx_conn = f""" @@ -983,45 +906,28 @@ module manta ( return interface_tx_conn + interface_tx_inst - def generate_footer(self): + def gen_footer(self): return """endmodule\n""" - def generate_module_defs(self): + def gen_module_defs(self): # aggregate module definitions and remove duplicates module_defs_with_dups = [self.interface.rx_hdl_def()] + [core.hdl_def() for core in self.cores] + [self.interface.tx_hdl_def()] module_defs = [] module_defs = [m_def for m_def in module_defs_with_dups if m_def not in module_defs] return '\n'.join(module_defs) - - def generate_hdl(self, output_filepath): - # generate header - header = self.generate_header() - - # generate example instantiation - ex_inst = self.generate_ex_inst() - - # generate module declaration - declar = self.generate_declaration() - - # generate interface_rx - interface_rx = self.generate_interface_rx() - - # generate core chain - core_chain = self.generate_core_chain() - - # generate interface_tx - interface_tx = self.generate_interface_tx() - - # generate footer - footer = self.generate_footer() - - # generate module definitions - module_defs = self.generate_module_defs() + header = self.gen_header() + ex_inst = self.gen_example_inst() + declaration = self.gen_declaration() + "\n" + interface_rx = self.gen_interface_rx() + core_chain = self.gen_core_chain() + interface_tx = self.gen_interface_tx() + footer = self.gen_footer() + module_defs = self.gen_module_defs() # assemble all the parts - hdl = header + ex_inst + declar + interface_rx + core_chain + interface_tx + footer + hdl = header + ex_inst + declaration + interface_rx + core_chain + interface_tx + footer hdl += "\n /* ---- Module Definitions ---- */\n" hdl += module_defs diff --git a/src/manta/io_core_def_tmpl.v b/src/manta/io_core_def_tmpl.v new file mode 100644 index 0000000..a5fb8ef --- /dev/null +++ b/src/manta/io_core_def_tmpl.v @@ -0,0 +1,51 @@ +module /* MODULE_NAME */ ( + input wire clk, + + // ports + /* TOP_LEVEL_PORTS */ + + // input port + input wire [15:0] addr_i, + input wire [15:0] wdata_i, + input wire [15:0] rdata_i, + input wire rw_i, + input wire valid_i, + + // output port + output reg [15:0] addr_o, + output reg [15:0] wdata_o, + output reg [15:0] rdata_o, + output reg rw_o, + output reg valid_o + ); + + parameter BASE_ADDR = 0; + always @(posedge clk) begin + addr_o <= addr_i; + wdata_o <= wdata_i; + rdata_o <= rdata_i; + rw_o <= rw_i; + valid_o <= valid_i; + rdata_o <= rdata_i; + + + // check if address is valid + if( (valid_i) && (addr_i >= BASE_ADDR) && (addr_i <= BASE_ADDR + /* MAX_ADDR */)) begin + + // reads + if(!rw_i) begin + case (addr_i) + /* READ_CASE_STATEMENT_BODY */ + endcase + end + + // writes + else begin + case (addr_i) + /* WRITE_CASE_STATEMENT_BODY */ + endcase + end + end + end + +endmodule \ No newline at end of file diff --git a/src/manta/io_core_inst_tmpl.v b/src/manta/io_core_inst_tmpl.v new file mode 100644 index 0000000..b136b3a --- /dev/null +++ b/src/manta/io_core_inst_tmpl.v @@ -0,0 +1,20 @@ +/* MODULE_NAME */ /* INST_NAME */ ( + .clk(clk), + + // ports + /* INST_PORTS */ + + // input port + .addr_i(), + .wdata_i(), + .rdata_i(), + .rw_i(), + .valid_i(), + + // output port + .addr_o(), + .wdata_o(), + .rdata_o(), + .rw_o(), + .valid_o() + ); \ No newline at end of file diff --git a/src/manta/logic_analyzer_inst_tmpl.v b/src/manta/logic_analyzer_inst_tmpl.v new file mode 100644 index 0000000..d2c00bf --- /dev/null +++ b/src/manta/logic_analyzer_inst_tmpl.v @@ -0,0 +1,16 @@ +logic_analyzer /* INST_NAME */ ( + .clk(clk), + + .addr_i(), + .wdata_i(), + .rdata_i(), + .rw_i(), + .valid_i(), + + /* NET_CONNS */ + + .addr_o(), + .wdata_o(), + .rdata_o(), + .rw_o(), + .valid_o()); \ No newline at end of file diff --git a/src/manta/logic_analyzer_template.v b/src/manta/logic_analyzer_tmpl.v similarity index 100% rename from src/manta/logic_analyzer_template.v rename to src/manta/logic_analyzer_tmpl.v diff --git a/src/manta/lut_ram_inst_tmpl.v b/src/manta/lut_ram_inst_tmpl.v new file mode 100644 index 0000000..2cff8e3 --- /dev/null +++ b/src/manta/lut_ram_inst_tmpl.v @@ -0,0 +1,14 @@ +lut_ram #(.DEPTH(/* DEPTH */)) /* INST_NAME */ ( + .clk(clk), + + .addr_i(), + .wdata_i(), + .rdata_i(), + .rw_i(), + .valid_i(), + + .addr_o(), + .wdata_o(), + .rdata_o(), + .rw_o(), + .valid_o()); \ No newline at end of file diff --git a/src/manta/sample_mem_template.v b/src/manta/sample_mem_tmpl.v similarity index 100% rename from src/manta/sample_mem_template.v rename to src/manta/sample_mem_tmpl.v diff --git a/src/manta/trigger_block_inst_tmpl.v b/src/manta/trigger_block_inst_tmpl.v new file mode 100644 index 0000000..38e9487 --- /dev/null +++ b/src/manta/trigger_block_inst_tmpl.v @@ -0,0 +1,12 @@ +/* OP_DEC */ +/* ARG_DEC */ +/* TRIG_DEC */ + +trigger #(.INPUT_WIDTH(/* INPUT_WIDTH */)) /* NAME */ ( + .clk(clk), + + .probe(/* PROBE */), + .op(/* OP */), + .arg(/* ARG */), + .trig(/* TRIG */) +); diff --git a/src/manta/trigger_block_template.v b/src/manta/trigger_block_template.v index 8b429d7..49bfbaa 100644 --- a/src/manta/trigger_block_template.v +++ b/src/manta/trigger_block_template.v @@ -5,7 +5,7 @@ module trigger_block ( input wire clk, // probes - // @PROBE_PORTS + /* PROBE_PORTS */ // trigger output reg trig, @@ -25,15 +25,15 @@ module trigger_block ( output reg valid_o); parameter BASE_ADDR = 0; - localparam MAX_ADDR = // @MAX_ADDR; + localparam MAX_ADDR = /* MAX_ADDR */; // trigger configuration registers // - each probe gets an operation and a compare register // - at the end we OR them all together. along with any custom probes the user specs - // @TRIGGER_MODULE_INSTS + /* TRIGGER_MODULE_INSTS */ - // @COMBINE_INDIV_TRIGGERS + /* COMBINE_INDIV_TRIGGERS */ // perform register operations always @(posedge clk) begin @@ -49,14 +49,14 @@ module trigger_block ( // reads if(valid_i && !rw_i) begin case (addr_i) - // @READ_CASE_STATEMENT_BODY + /* READ_CASE_STATEMENT_BODY */ endcase end // writes else if(valid_i && rw_i) begin case (addr_i) - // @WRITE_CASE_STATEMENT_BODY + /* WRITE_CASE_STATEMENT_BODY */ endcase end end diff --git a/src/manta/uart_rx_bridge_rx_inst_templ.v b/src/manta/uart_rx_bridge_rx_inst_templ.v new file mode 100644 index 0000000..610930b --- /dev/null +++ b/src/manta/uart_rx_bridge_rx_inst_templ.v @@ -0,0 +1,19 @@ +rx_uart #(.CLOCKS_PER_BAUD(/* CLOCKS_PER_BAUD */)) urx ( + .i_clk(clk), + .i_uart_rx(rx), + .o_wr(urx_brx_axiv), + .o_data(urx_brx_axid)); + +logic [7:0] urx_brx_axid; +logic urx_brx_axiv; + +bridge_rx brx ( + .clk(clk), + + .rx_data(urx_brx_axid), + .rx_valid(urx_brx_axiv), + + .addr_o(), + .wdata_o(), + .rw_o(), + .valid_o()); \ No newline at end of file diff --git a/src/manta/uart_tx_bridge_tx_inst_templ.v b/src/manta/uart_tx_bridge_tx_inst_templ.v new file mode 100644 index 0000000..fe26827 --- /dev/null +++ b/src/manta/uart_tx_bridge_tx_inst_templ.v @@ -0,0 +1,23 @@ +bridge_tx btx ( + .clk(clk), + + .rdata_i(), + .rw_i(), + .valid_i(), + + .ready_i(utx_btx_ready), + .data_o(btx_utx_data), + .valid_o(btx_utx_valid)); + +logic utx_btx_ready; +logic btx_utx_valid; +logic [7:0] btx_utx_data; + +uart_tx #(.CLOCKS_PER_BAUD(/* CLOCKS_PER_BAUD */)) utx ( + .clk(clk), + + .data(btx_utx_data), + .valid(btx_utx_valid), + .ready(utx_btx_ready), + + .tx(tx)); \ No newline at end of file