diff --git a/Makefile b/Makefile index a6dabd9..93fd1aa 100644 --- a/Makefile +++ b/Makefile @@ -49,3 +49,6 @@ clean: rm -f *.out *.vcd rm -rf dist/ rm -rf src/mantaray.egg-info + +loc: + find . -type f \( -iname \*.sv -o -iname \*.v -o -iname \*.py -o -iname \*.yaml -o -iname \*.md \) | sed 's/.*/"&"/' | xargs wc -l \ No newline at end of file diff --git a/src/inspecto_time.sv b/src/inspecto_time.sv deleted file mode 100644 index 17812c4..0000000 --- a/src/inspecto_time.sv +++ /dev/null @@ -1,471 +0,0 @@ -`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 6bd1853..ef25d7a 100644 --- a/src/manta/__init__.py +++ b/src/manta/__init__.py @@ -103,8 +103,7 @@ class UARTInterface: .valid(btx_utx_valid), .ready(utx_btx_ready), - .tx(utx_tb_tx)); - """ + .tx(utx_tb_tx));\n""" class IOCore: @@ -138,20 +137,20 @@ class LogicAnalyzerCore: def hdl_inst(self): hdl = f""" - la_core {self.name} ( - .clk(), + la_core {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\n""" + .addr_i(), + .wdata_i(), + .rdata_i(), + .rw_i(), + .valid_i(), + + .addr_o(), + .wdata_o(), + .rdata_o(), + .rw_o(), + .valid_o());\n\n""" return hdl @@ -262,6 +261,10 @@ class LogicAnalyzerCore: tmpl = tmpl.replace("@SAMPLE_DEPTH", str(self.sample_depth)) return tmpl + def hdl_top_level_ports(self): + # this should return the probes that we want to connect to top-level, but like as a string of verilog\ + return "" # TODO: i'll fix this later + class Manta: def __init__(self, config_filepath): @@ -343,7 +346,7 @@ class Manta: return conns - def generate_instantiations(self): + def generate_instances(self): # generates hdl for modules that need to be connected together insts = [] @@ -372,46 +375,9 @@ class Manta: insts.append(hdl) return insts - - def generate_hdl(self): - """ - anatomy of manta.v: - - 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 - """ - - # generate header - user = os.environ.get("USER", os.environ.get("USERNAME")) - timestamp = datetime.now().strftime("%d %b %Y at %H:%M:%S") - - 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() + def generate_core_chain(self): + insts = self.generate_instances() conns = self.generate_connections() core_chain = [] for i, inst in enumerate(insts): @@ -419,23 +385,130 @@ class Manta: if (i != len(insts)-1): core_chain.append(conns[i]) + + return '\n'.join(core_chain) - core_chain = '\n'.join(core_chain) + def generate_header(self): + # generate header + user = os.environ.get("USER", os.environ.get("USERNAME")) + timestamp = datetime.now().strftime("%d %b %Y at %H:%M:%S") - # generate interface_tx + header = f""" +/* +This manta definition was generated on {timestamp} by {user} + +If this breaks or if you've got dank formal verification memes, +please contact fischerm [at] mit.edu + +Provided under a GNU GPLv3 license. Go wild. +*/ +""" + return header + + def generate_declaration(self): + # get all the top level connections for each module. + + #ports = [core.top_level_ports() for core in self.cores] + #ports = "\n".join(ports) + + return f""" +module manta ( + input wire clk, + input wire rx, + output reg tx, +);""" + + def generate_interface_rx(self): + interface_rx = self.interface.rx_hdl_inst() + # connect interface_rx to core_chain + interface_rx_chain_connection = f""" + reg [15:0] brx_{self.cores[0].name}_addr; + reg [15:0] brx_{self.cores[0].name}_wdata; + reg brx_{self.cores[0].name}_rw; + reg brx_{self.cores[0].name}_valid;\n""" + + interface_rx = interface_rx.replace("addr_o()", f"addr_o(brx_{self.cores[0].name}_addr)") + interface_rx = interface_rx.replace("wdata_o()", f"wdata_o(brx_{self.cores[0].name}_wdata)") + interface_rx = interface_rx.replace("rw_o()", f"rw_o(brx_{self.cores[0].name}_rw)") + interface_rx = interface_rx.replace("valid_o()", f"valid_o(brx_{self.cores[0].name}_valid)") + + return interface_rx + + def generate_interface_tx(self): interface_tx = self.interface.tx_hdl_inst() - # generate footer - footer = """endmodule\n""" + interface_tx = interface_tx.replace("addr_i()", f"addr_o({self.cores[0].name}_btx_addr)") + interface_tx = interface_tx.replace("rdata_i()", f"rdata_o({self.cores[0].name}_btx_rdata)") + interface_tx = interface_tx.replace("rw_i()", f"rw_o({self.cores[0].name}_btx_rw)") + interface_tx = interface_tx.replace("valid_i()", f"valid_o({self.cores[0].name}_btx_valid)") + + return interface_tx + def generate_footer(self): + return """endmodule\n""" + + def generate_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] - module_defs = '\n'.join(module_defs) - + return '\n'.join(module_defs) + + + + def generate_hdl(self, output_filepath): + """ + This function generates manta.v, which has the following anatomy: + - Header - contains a little blurb about when and who generated the file + - Top-Level Module - the actual definition of module manta + - Declaration - contains `module manta` and top-level ports + that constitutent cores need access to + - Interface RX - the modules needed to bring whatever interface the user + selected onto the bus. For UART, this is just an instance + of uart_rx and bridge_rx. + - Core Chain - the chain of cores specified by the user. This follows + a sequence of: + - Core Instance - HDL specifying an instance of the core. + - Core Connection - HDL specifying the registers that connect one + core to the next. + - Core Instance + - Core Connection + .... + + This repeats for however many cores the user specified. + + - Interface TX - the modules needed to bring the bus out to whatever + interface the user selected. For UART, this is just + an instance of bridge_tx and uart_tx. + - Footer - just the 'endmodule' keyword. + + - Module Definitions - all the source for the modules instantiated in the + top-level module. + """ + + # generate header + header = self.generate_header() + + # 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() + # assemble all the parts - hdl = header + interface_rx + core_chain + interface_tx + footer + hdl = header + declar + interface_rx + core_chain + interface_tx + footer hdl += "\n /* ---- Module Definitions ---- */\n" hdl += module_defs @@ -445,7 +518,9 @@ class Manta: hdl = hdl.replace("`timescale 1ns/1ps", "") hdl = "`default_nettype none\n" + "`timescale 1ns/1ps\n" + hdl + "`default_nettype wire" - return hdl + + with open(output_filepath, 'w') as f: + f.write(hdl) def main(): diff --git a/src/test.py b/src/test.py index 1b074ae..a8e5146 100644 --- a/src/test.py +++ b/src/test.py @@ -1,4 +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 +m.generate_hdl('inspecto_time.v') \ No newline at end of file