diff --git a/.gitignore b/.gitignore index 033dfa9..9277988 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,9 @@ # macOS being lame .DS_Store +# VSCode being lame +*.vscode/ + # Vivado output products *.log *.jou diff --git a/src/manta/__init__.py b/src/manta/__init__.py index 6aad6a6..1709de7 100644 --- a/src/manta/__init__.py +++ b/src/manta/__init__.py @@ -467,11 +467,108 @@ class LogicAnalyzerCore: return hdl + def generate_trigger_block(self): + trigger_block_hdl = pkgutil.get_data(__name__, "trigger_block_template.v").decode() + + # 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 + "," + 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 + trigger_module_insts = [] + for name, width in self.probes.items(): + trigger_module_inst = f"reg [3:0] {name}_trigger_op = 0;\n\t" + + if width == 1: + trigger_module_inst += f"reg {name}_trigger_arg = 0;\n\t" + + else: + trigger_module_inst += f"reg [{width-1}:0] {name}_trigger_arg = 0;\n\t" + + trigger_module_inst += f"reg {name}_trig;\n\t" + trigger_module_inst += f""" + trigger #(.INPUT_WIDTH({width})) {name}_trigger ( + .clk(clk), + + .probe({name}), + .op({name}_trigger_op), + .arg({name}_trigger_arg), + .trig({name}_trig) + ); + """ + trigger_module_insts.append(trigger_module_inst) + + 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) + + # 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) + + # add read and write block case statement bodies + rcsb = "" # read case statement body + wcsb = "" # write case statement body + 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" + + 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 = 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 + + def hdl_def(self): + # Return an autogenerated verilog module definition for the core. + # load source files + logic_analyzer_hdl = pkgutil.get_data(__name__, "logic_analyzer.v").decode() + la_fsm_hdl = pkgutil.get_data(__name__, "la_fsm.v").decode() + sample_mem_hdl = pkgutil.get_data(__name__, "sample_mem.v").decode() + xilinx_bram_hdl = pkgutil.get_data(__name__, "xilinx_true_dual_port_read_first_2_clock_ram.v").decode() + trigger_hdl = pkgutil.get_data(__name__, "trigger.v").decode() + + # generate trigger block + trigger_block_hdl = self.generate_trigger_block() + + return logic_analyzer_hdl + la_fsm_hdl + sample_mem_hdl + xilinx_bram_hdl + trigger_block_hdl + trigger_hdl + + 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 + + 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 + def run(self): - self.interface.open() - self.interface.flushInput() - self.interface.write(b"\x30") - data = self.interface.read(4096) + pass def part_select(self, data, width): top, bottom = width @@ -538,29 +635,6 @@ class LogicAnalyzerCore: writer.change(probe, timestamp, val) vcd_file.close() - def hdl_def(self): - # Return an autogenerated verilog module definition for the core. - # load source files - logic_analyzer_hdl = pkgutil.get_data(__name__, "logic_analyzer.v").decode() - la_fsm_hdl = pkgutil.get_data(__name__, "la_fsm.v").decode() - sample_mem_hdl = pkgutil.get_data(__name__, "sample_mem.v").decode() - xilinx_bram_hdl = pkgutil.get_data(__name__, "xilinx_true_dual_port_read_first_2_clock_ram.v").decode() - trigger_block_hdl= pkgutil.get_data(__name__, "trigger_block.v").decode() - trigger_hdl = pkgutil.get_data(__name__, "trigger.v").decode() - - return logic_analyzer_hdl + la_fsm_hdl + sample_mem_hdl + xilinx_bram_hdl + trigger_block_hdl + trigger_hdl - - 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 - - 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 class Manta: def __init__(self, config_filepath): diff --git a/src/manta/trigger_block_template.v b/src/manta/trigger_block_template.v new file mode 100644 index 0000000..34741e8 --- /dev/null +++ b/src/manta/trigger_block_template.v @@ -0,0 +1,66 @@ +`default_nettype none +`timescale 1ns/1ps + +module trigger_block ( + input wire clk, + + // probes + // @PROBE_PORTS + + // trigger + output reg trig, + + // 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; + 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 + + // @COMBINE_INDIV_TRIGGERS + + // perform register operations + 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; + + if( (addr_i >= BASE_ADDR) && (addr_i <= BASE_ADDR + MAX_ADDR) ) begin + + // reads + if(valid_i && !rw_i) begin + case (addr_i) + // @READ_CASE_STATEMENT_BODY + endcase + end + + // writes + else if(valid_i && rw_i) begin + case (addr_i) + // @WRITE_CASE_STATEMENT_BODY + endcase + end + end + end +endmodule + +`default_nettype wire \ No newline at end of file