From 9dba38925baeaf6e79d6d3fe348ff2051c434035 Mon Sep 17 00:00:00 2001 From: Fischer Moseley <42497969+fischermoseley@users.noreply.github.com> Date: Wed, 8 Mar 2023 21:06:56 -0500 Subject: [PATCH] add module definitions to generated hdl --- src/inspecto_time.sv | 471 ++++++++++++++++++++++++++++++++++++++++ src/manta/__init__.py | 158 +++++++++++--- src/manta/la_template.v | 0 src/manta/uart_tx.v | 2 +- src/test.py | 4 + 5 files changed, 599 insertions(+), 36 deletions(-) create mode 100644 src/inspecto_time.sv create mode 100644 src/manta/la_template.v create mode 100644 src/test.py diff --git a/src/inspecto_time.sv b/src/inspecto_time.sv new file mode 100644 index 0000000..17812c4 --- /dev/null +++ b/src/inspecto_time.sv @@ -0,0 +1,471 @@ +`default_nettype none +`timescale 1ns/1ps +/* This manata definition was generated on 08 Mar 2023 at 21:04:09 by fischerm + * + * If this breaks or if you've got dank formal verification memes, + * please contact fischerm [at] mit.edu + */ + + rx_uart #(.CLOCKS_PER_BAUD(868)) urx ( + .i_clk(clk), + .i_uart_rx(tb_urx_rxd), + .o_wr(urx_brx_axiv), + .o_data(urx_brx_axid)); + + // uart_rx --> bridge_rx signals + 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()); + + la_core my_logic_analyzer ( + .clk(), + + .addr_i(), + .wdata_i(), + .rdata_i(), + .rw_i(), + .valid_i(), + + .addr_o(), + .wdata_o(), + .rdata_o(), + .rw_o(), + .valid_o()); + + + 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(868)) utx ( + .clk(clk), + + .data(btx_utx_data), + .valid(btx_utx_valid), + .ready(utx_btx_ready), + + .tx(utx_tb_tx)); + endmodule + + /* ---- Module Definitions ---- */ +//////////////////////////////////////////////////////////////////////////////// +// +// Filename: rxuart.v +// +// Project: Verilog Tutorial Example file +// +// Purpose: Receives a character from a UART (serial port) wire. Key +// features of this core include: +// +// - The baud rate is constant, and set by the CLOCKS_PER_BAUD parameter. +// To be successful, one baud interval must be (approximately) +// equal to CLOCKS_PER_BAUD / CLOCK_RATE_HZ seconds long. +// +// - The protocol used is the basic 8N1: 8 data bits, 1 stop bit, and no +// parity. +// +// - This core has no reset +// - This core has no error detection for frame errors +// - This core cannot detect, report, or even recover from, a break +// condition on the line. A break condition is defined as a +// period of time where the i_uart_rx line is held low for longer +// than one data byte (10 baud intervals) +// +// - There's no clock rate detection in this core +// +// Perhaps one of the nicer features of this core is that it (can be) +// formally verified. It depends upon a separate (formally verified) +// transmit core for this purpose. +// +// As with the other cores within this tutorial, there may (or may not) be +// bugs within this design for you to find. +// +// +// Creator: Dan Gisselquist, Ph.D. +// Gisselquist Technology, LLC +// +//////////////////////////////////////////////////////////////////////////////// +// +// Written and distributed by Gisselquist Technology, LLC +// +// This program is hereby granted to the public domain. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or +// FITNESS FOR A PARTICULAR PURPOSE. +// +//////////////////////////////////////////////////////////////////////////////// +// +// + + +module rx_uart( + input wire i_clk, + input wire i_uart_rx, + output reg o_wr, + output reg [7:0] o_data); + + parameter [15:0] CLOCKS_PER_BAUD = 868; + localparam [3:0] IDLE = 4'h0; + localparam [3:0] BIT_ZERO = 4'h1; + // localparam [3:0] BIT_ONE = 4'h2; + // localparam [3:0] BIT_TWO = 4'h3; + // localparam [3:0] BIT_THREE = 4'h4; + // localparam [3:0] BIT_FOUR = 4'h5; + // localparam [3:0] BIT_FIVE = 4'h6; + // localparam [3:0] BIT_SIX = 4'h7; + // localparam [3:0] BIT_SEVEN = 4'h8; + localparam [3:0] STOP_BIT = 4'h9; + + reg [3:0] state; + reg [15:0] baud_counter; + reg zero_baud_counter; + + // 2FF Synchronizer + // + reg ck_uart; + reg q_uart; + initial { ck_uart, q_uart } = -1; + always @(posedge i_clk) + { ck_uart, q_uart } <= { q_uart, i_uart_rx }; + + initial state = IDLE; + initial baud_counter = 0; + + always @(posedge i_clk) + if (state == IDLE) begin + state <= IDLE; + baud_counter <= 0; + if (!ck_uart) begin + state <= BIT_ZERO; + baud_counter <= CLOCKS_PER_BAUD+CLOCKS_PER_BAUD/2-1'b1; + end + end + + else if (zero_baud_counter) begin + state <= state + 1; + baud_counter <= CLOCKS_PER_BAUD-1'b1; + if (state == STOP_BIT) begin + state <= IDLE; + baud_counter <= 0; + end + end + + else baud_counter <= baud_counter - 1'b1; + + always @(*) + zero_baud_counter = (baud_counter == 0); + + always @(posedge i_clk) + if ((zero_baud_counter)&&(state != STOP_BIT)) + o_data <= { ck_uart, o_data[7:1] }; + + initial o_wr = 1'b0; + always @(posedge i_clk) + o_wr <= ((zero_baud_counter)&&(state == STOP_BIT)); + +endmodule + + + + +module bridge_rx( + input wire clk, + + input wire[7:0] rx_data, + input wire rx_valid, + + output reg[15:0] addr_o, + output reg[15:0] wdata_o, + output reg rw_o, + output reg valid_o +); + + +// this is a hack, the FSM needs to be updated +// but this will bypass it for now +parameter ready_i = 1; + +parameter ADDR_WIDTH = 0; +parameter DATA_WIDTH = 0; + +localparam PREAMBLE = 8'h4D; +localparam CR = 8'h0D; +localparam LF = 8'h0A; + +localparam ACQUIRE = 0; +localparam TRANSMIT = 1; +localparam ERROR = 2; + +reg [1:0] state; +reg [3:0] bytes_received; + +// no global resets! +initial begin + addr_o = 0; + wdata_o = 0; + rw_o = 0; + valid_o = 0; + bytes_received = 0; + state = ACQUIRE; +end + +reg [3:0] rx_data_decoded; +reg rx_data_is_0_thru_9; +reg rx_data_is_A_thru_F; + +always @(*) begin + rx_data_is_0_thru_9 = (rx_data >= 8'h30) & (rx_data <= 8'h39); + rx_data_is_A_thru_F = (rx_data >= 8'h41) & (rx_data <= 8'h46); + + if (rx_data_is_0_thru_9) rx_data_decoded = rx_data - 8'h30; + else if (rx_data_is_A_thru_F) rx_data_decoded = rx_data - 8'h41 + 'd10; + else rx_data_decoded = 0; +end + + +always @(posedge clk) begin + if (state == ACQUIRE) begin + if(rx_valid) begin + + if (bytes_received == 0) begin + if(rx_data == PREAMBLE) bytes_received <= 1; + end + + else if( (bytes_received >= 1) & (bytes_received <= 4) ) begin + // only advance if byte is valid hex digit + if(rx_data_is_0_thru_9 | rx_data_is_A_thru_F) begin + addr_o <= (addr_o << 4) | rx_data_decoded; + bytes_received <= bytes_received + 1; + end + + else state <= ERROR; + end + + else if( bytes_received == 5) begin + if( (rx_data == CR) | (rx_data == LF)) begin + valid_o <= 1; + rw_o = 0; + bytes_received <= 0; + state <= TRANSMIT; + end + + else if (rx_data_is_0_thru_9 | rx_data_is_A_thru_F) begin + bytes_received <= bytes_received + 1; + wdata_o <= (wdata_o << 4) | rx_data_decoded; + end + + else state <= ERROR; + end + + else if ( (bytes_received >= 6) & (bytes_received <= 8) ) begin + + if (rx_data_is_0_thru_9 | rx_data_is_A_thru_F) begin + wdata_o <= (wdata_o << 4) | rx_data_decoded; + bytes_received <= bytes_received + 1; + end + + else state <= ERROR; + end + + else if (bytes_received == 9) begin + bytes_received <= 0; + if( (rx_data == CR) | (rx_data == LF)) begin + valid_o <= 1; + rw_o <= 1; + state <= TRANSMIT; + end + + else state <= ERROR; + end + end + end + + + else if (state == TRANSMIT) begin + if(ready_i) begin + valid_o <= 0; + state <= ACQUIRE; + end + + if(rx_valid) begin + if ( (rx_data != CR) & (rx_data != LF)) begin + valid_o <= 0; + state <= ERROR; + end + end + end +end + +endmodule + + + + + +module bridge_tx( + input wire clk, + + input wire [15:0] rdata_i, + input wire rw_i, + input wire valid_i, + + output reg [7:0] data_o, + input wire ready_i, + output reg valid_o); + +localparam PREAMBLE = 8'h4D; +localparam CR = 8'h0D; +localparam LF = 8'h0A; + +logic busy; +logic [15:0] buffer; +logic [3:0] byte_counter; + +initial begin + busy = 0; + buffer = 0; + byte_counter = 0; + valid_o = 0; +end + +always @(posedge clk) begin + if (!busy) begin + if (valid_i && !rw_i) begin + busy <= 1; + buffer <= rdata_i; + byte_counter <= 0; + valid_o <= 1; + end + end + + if (busy) begin + + if(ready_i) begin + byte_counter <= byte_counter + 1; + + if (byte_counter > 5) begin + byte_counter <= 0; + + // stop transmitting if we don't have both valid and read + if ( !(valid_i && !rw_i) ) begin + busy <= 0; + valid_o <= 0; + end + end + end + end +end + +always @(*) begin + case (byte_counter) + 0: data_o = PREAMBLE; + 1: data_o = (buffer[15:12] < 10) ? (buffer[15:12] + 8'h30) : (buffer[15:12] + 8'h41 - 'd10); + 2: data_o = (buffer[11:8] < 10) ? (buffer[11:8] + 8'h30) : (buffer[11:8] + 8'h41 - 'd10); + 3: data_o = (buffer[7:4] < 10) ? (buffer[7:4] + 8'h30) : (buffer[7:4] + 8'h41 - 'd10); + 4: data_o = (buffer[3:0] < 10) ? (buffer[3:0] + 8'h30) : (buffer[3:0] + 8'h41 - 'd10); + 5: data_o = CR; + 6: data_o = LF; + default: data_o = 0; + endcase +end + +endmodule + + + + +module uart_tx( + input wire clk, + + input wire [7:0] data, + input wire valid, + output reg busy, + output reg ready, + + output reg tx); + + // this transmitter only works with 8N1 serial, at configurable baudrate + parameter CLOCKS_PER_BAUD = 868; + + reg [9:0] baud_counter; + reg [8:0] data_buf; + reg [3:0] bit_index; + + initial begin + baud_counter = CLOCKS_PER_BAUD; + data_buf = 0; + bit_index = 0; + busy = 0; + ready = 1; + tx = 1; + end + + always @(posedge clk) begin + if (valid && !busy) begin + data_buf <= {1'b1, data}; + bit_index <= 0; + tx <= 0; //wafflestomp that start bit + baud_counter <= CLOCKS_PER_BAUD - 1; + busy <= 1; + ready <= 0; + end + + else if (busy) begin + baud_counter <= baud_counter - 1; + + ready <= (baud_counter == 1) && (bit_index == 9); + + if (baud_counter == 0) begin + baud_counter <= CLOCKS_PER_BAUD - 1; + + + if (bit_index == 9) begin + if(valid) begin + data_buf <= {1'b1, data}; + bit_index <= 0; + tx <= 0; + end + + else begin + busy <= 0; + ready <= 1; + end + // if valid happens here then we should bool + end + + else begin + tx <= data_buf[bit_index]; + bit_index <= bit_index + 1; + end + end + end + end + + + +endmodule + + +`default_nettype wire diff --git a/src/manta/__init__.py b/src/manta/__init__.py index f0c14f8..6bd1853 100644 --- a/src/manta/__init__.py +++ b/src/manta/__init__.py @@ -43,13 +43,68 @@ class UARTInterface: self.ser.read(bytes) def write(self, bytes): - self.ser.write(bytes) + self.ser.write(bytes) - def tx_hdl(self): - pkgutil.get_data(__name__, "uart_tx.sv").decode() + 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() + return uart_rx_def + '\n' + bridge_rx_def - def rx_hdl(self): - pkgutil.get_data(__name__, "rx_uart.sv").decode() + 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() + 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(tb_urx_rxd), + .o_wr(urx_brx_axiv), + .o_data(urx_brx_axid)); + + // uart_rx --> bridge_rx signals + 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()); + """ + + 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(utx_tb_tx)); + """ class IOCore: @@ -81,7 +136,7 @@ class LogicAnalyzerCore: assert len(config["triggers"]) > 0, "Must specify at least one trigger." self.triggers = config["triggers"] - def inst(self): + def hdl_inst(self): hdl = f""" la_core {self.name} ( .clk(), @@ -171,7 +226,7 @@ class LogicAnalyzerCore: writer.change(probe, timestamp, val) vcd_file.close() - def hdl(self): + def hdl_def(self): # Return an autogenerated verilog module definition for the core. # load source files tmpl = pkgutil.get_data(__name__, "la_template.v").decode() @@ -179,13 +234,13 @@ class LogicAnalyzerCore: # add triggers trigger = [f"({trigger})" for trigger in self.triggers] trigger = " || ".join(trigger) - templ = templ.replace("@TRIGGER", trigger) + tmpl = tmpl.replace("@TRIGGER", trigger) # add concat concat = [name for name in self.probes] concat = ", ".join(concat) concat = "{" + concat + "}" - templ = templ.replace("@CONCAT", concat) + tmpl = tmpl.replace("@CONCAT", concat) # add probes probe_verilog = [] @@ -295,7 +350,7 @@ class Manta: for i, core in enumerate(self.cores): # should probably check if core is LogicAnalyzerCore or IOCore - hdl = core.inst() + hdl = core.hdl_inst() if (i < len(self.cores)-1): dst = self.cores[i+1] @@ -318,45 +373,78 @@ class Manta: return insts - def generate(self): - # this occurs in two steps: generating manta and the top-level, - # and pasting in all the HDL from earlier. - - uart_rx_hdl = pkgutil.get_data(__name__, "rx_uart.v").decode() - bridge_rx_hdl = pkgutil.get_data(__name__, "bridge_rx.v").decode() - bridge_tx_hdl = pkgutil.get_data(__name__, "bridge_tx.v").decode() - uart_tx_hdl = pkgutil.get_data(__name__, "uart_tx.v").decode() - - + def generate_hdl(self): """ - ok so the way this works is that we have two lists, instantiations and connections - we make connections first, and it works by pairwise iterating over pairs of cores and - doing the 'reg[N:0] src_dest_name' thing + anatomy of manta.v: - once we have that, we then generate the instantiations. we iterate through the same pairs, - setting outputs on the src, and inputs on the dest. this leaves the inputs of the first node - and the outputs of the last node unconnected, but we'll have a final step to take care of that. + - header + - top-level module: + - module declaration + - top <-> cores + - interface_in (for uart, this would be uart_rx and bridge_rx) + + - core chain + - core inst + - core connection + - core inst + - core connection + .... + + - interface_out (for uart, this is bridge_tx and uart_tx) + - footer + + + then come all the module definitions """ - - # add preamble to top of file + # generate header user = os.environ.get("USER", os.environ.get("USERNAME")) timestamp = datetime.now().strftime("%d %b %Y at %H:%M:%S") - hdl = f"/* This manata definition was generated on {timestamp} by {user}\n" - hdl += " *\n" - hdl += " * If this breaks or if you've got dank formal verification memes,\n" - hdl += " * please contact fischerm [at] mit.edu\n" - hdl += " */\n" + header = f"/* This manata definition was generated on {timestamp} by {user}\n" + header += " *\n" + header += " * If this breaks or if you've got dank formal verification memes,\n" + header += " * please contact fischerm [at] mit.edu\n" + header += " */\n" + # generate interface_rx + interface_rx = self.interface.rx_hdl_inst() + + # generate chain of cores insts = self.generate_instantiations() conns = self.generate_connections() + core_chain = [] for i, inst in enumerate(insts): - hdl += inst + core_chain.append(inst) if (i != len(insts)-1): - hdl += conns[i] + core_chain.append(conns[i]) + core_chain = '\n'.join(core_chain) + + # generate interface_tx + interface_tx = self.interface.tx_hdl_inst() + + # generate footer + footer = """endmodule\n""" + + # 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] + module_defs = '\n'.join(module_defs) + + # assemble all the parts + hdl = header + interface_rx + core_chain + interface_tx + footer + hdl += "\n /* ---- Module Definitions ---- */\n" + hdl += module_defs + + # default_nettype and timescale directives only at the beginning and end + hdl = hdl.replace("`default_nettype none", "") + hdl = hdl.replace("`default_nettype wire", "") + hdl = hdl.replace("`timescale 1ns/1ps", "") + + hdl = "`default_nettype none\n" + "`timescale 1ns/1ps\n" + hdl + "`default_nettype wire" return hdl diff --git a/src/manta/la_template.v b/src/manta/la_template.v new file mode 100644 index 0000000..e69de29 diff --git a/src/manta/uart_tx.v b/src/manta/uart_tx.v index 920e98c..a37f1a6 100644 --- a/src/manta/uart_tx.v +++ b/src/manta/uart_tx.v @@ -1,5 +1,5 @@ `default_nettype none -`timescale 1ns / 1ps +`timescale 1ns/1ps module uart_tx( input wire clk, diff --git a/src/test.py b/src/test.py new file mode 100644 index 0000000..1b074ae --- /dev/null +++ b/src/test.py @@ -0,0 +1,4 @@ +from manta import Manta + +m = Manta('/Users/fischerm/fpga/manta/examples/nexys_a7/single_lut_ram/manta.yaml') +print (m.generate_hdl()) \ No newline at end of file