diff --git a/Makefile b/Makefile index be12ae0..aba861f 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,12 @@ lint: python3 -m black src/manta/__init__.py python3 -m black src/manta/__main__.py -sim: bit_fifo_tb bridge_rx_tb bridge_tx_tb fifo_tb lut_ram_tb uart_tx_tb +sim: io_core_tb logic_analyzer_tb bit_fifo_tb bridge_rx_tb bridge_tx_tb fifo_tb lut_ram_tb uart_tb uart_tx_tb + +io_core_tb: + iverilog -g2012 -o sim.out test/io_core_tb.sv src/manta/io_core.v + vvp sim.out + rm sim.out logic_analyzer_tb: iverilog -g2012 -o sim.out test/logic_analyzer_tb.sv src/manta/logic_analyzer.v src/manta/la_fsm.v src/manta/trigger_block.v src/manta/trigger.v src/manta/sample_mem.v src/manta/xilinx_true_dual_port_read_first_2_clock_ram.v diff --git a/examples/nexys_a7/io_core/foo.v b/examples/nexys_a7/io_core/foo.v new file mode 100644 index 0000000..433a9b0 --- /dev/null +++ b/examples/nexys_a7/io_core/foo.v @@ -0,0 +1,473 @@ +`default_nettype none +`timescale 1ns/1ps + +/* +This manta definition was generated on 16 Mar 2023 at 09:36:48 by fischerm + +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. +*/ + +module manta ( + input wire clk, + + input wire rx, + output reg tx); + + rx_uart #(.CLOCKS_PER_BAUD(868)) 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(brx_my_io_core_addr), + .wdata_o(brx_my_io_core_wdata), + .rw_o(brx_my_io_core_rw), + .valid_o(brx_my_io_core_valid)); + + reg [15:0] brx_my_io_core_addr; + reg [15:0] brx_my_io_core_wdata; + reg brx_my_io_core_rw; + reg brx_my_io_core_valid; + + reg [15:0] my_io_core_btx_rdata; + reg my_io_core_btx_rw; + reg my_io_core_btx_valid; + + bridge_tx btx ( + .clk(clk), + + .rdata_i(my_io_core_btx_rdata), + .rw_i(my_io_core_btx_rw), + .valid_i(my_io_core_btx_valid), + + .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(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 \ No newline at end of file diff --git a/examples/nexys_a7/io_core/manta.yaml b/examples/nexys_a7/io_core/manta.yaml new file mode 100644 index 0000000..746ab1d --- /dev/null +++ b/examples/nexys_a7/io_core/manta.yaml @@ -0,0 +1,21 @@ +--- +cores: + my_io_core: + type: io + + inputs: + picard: 1 + data: 7 + laforge: 10 + troi: 1 + + outputs: + kirk: 1 + spock: 5 + uhura: 3 + chekov: 1 + +uart: + port: "/dev/tty.usbserial-2102926963071" + baudrate: 115200 + clock_freq: 100000000 \ No newline at end of file diff --git a/examples/nexys_a7/io_core/src/manta.v b/examples/nexys_a7/io_core/src/manta.v new file mode 100644 index 0000000..eda13af --- /dev/null +++ b/examples/nexys_a7/io_core/src/manta.v @@ -0,0 +1,498 @@ +`default_nettype none +`timescale 1ns/1ps + +/* +This manta definition was generated on 14 Mar 2023 at 13:06:49 by fischerm + +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. +*/ + +module manta ( + input wire clk, + + input wire rx, + output reg tx, + + input wire larry, + input wire curly, + input wire moe, + input wire [3:0] shemp); + + rx_uart #(.CLOCKS_PER_BAUD(868)) 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(brx_my_logic_analyzer_addr), + .wdata_o(brx_my_logic_analyzer_wdata), + .rw_o(brx_my_logic_analyzer_rw), + .valid_o(brx_my_logic_analyzer_valid)); + + reg [15:0] brx_my_logic_analyzer_addr; + reg [15:0] brx_my_logic_analyzer_wdata; + reg brx_my_logic_analyzer_rw; + reg brx_my_logic_analyzer_valid; + + la_core my_logic_analyzer ( + .clk(clk), + + .addr_i(brx_my_logic_analyzer_addr), + .wdata_i(brx_my_logic_analyzer_wdata), + .rdata_i(), + .rw_i(brx_my_logic_analyzer_rw), + .valid_i(brx_my_logic_analyzer_valid), + + .larry(larry), + .curly(curly), + .moe(moe), + .shemp(shemp), + + .addr_o(), + .wdata_o(), + .rdata_o(my_logic_analyzer_btx_rdata), + .rw_o(my_logic_analyzer_btx_rw), + .valid_o(my_logic_analyzer_btx_valid)); + + reg [15:0] my_logic_analyzer_btx_rdata; + reg my_logic_analyzer_btx_rw; + reg my_logic_analyzer_btx_valid; + + bridge_tx btx ( + .clk(clk), + + .rdata_i(my_logic_analyzer_btx_rdata), + .rw_i(my_logic_analyzer_btx_rw), + .valid_i(my_logic_analyzer_btx_valid), + + .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(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 \ No newline at end of file diff --git a/src/manta/__init__.py b/src/manta/__init__.py index 216f352..05d11e5 100644 --- a/src/manta/__init__.py +++ b/src/manta/__init__.py @@ -109,6 +109,131 @@ class UARTInterface: .tx(tx));\n""" +class IOCore: + def __init__(self, config, interface): + self.interface = interface + + # make sure we have ports defined + assert ('inputs' in config) or ('outputs' in config), "No input or output ports specified." + + # add inputs to core + address = 0 + self.inputs = [] + if 'inputs' in config: + for name, width in config["inputs"].items(): + # make sure inputs are of reasonable width + assert width > 0, f"Input {name} must have width greater than zero." + + self.inputs.append( {"name": name, "width": width, "address": address} ) + self.max_rel_addr = address + address += 1 + + # add outputs to core + self.outputs = [] + if 'outputs' in config: + for name, width in config["outputs"].items(): + # make sure inputs are of reasonable width + assert width > 0, f"Output {name} must have width greater than zero." + + self.outputs.append( {"name": name, "width": width, "address": address} ) + self.max_rel_addr = address + address += 1 + + def hdl_inst(self): + hdl = f"" + return hdl + + def hdl_def(self): + + # generate declaration + + # generate memory handling + read_case_statement_body = "" + for input in self.inputs: + name = input["name"] + width = input["width"] + address = input["address"] + + read_case_statement_body += f"\t\t\tBASE_ADDR + {address}: rdata_o <= {{{16-width}'b0, {name}}}\n" + + write_case_statement_body = "" + for output in self.outputs: + name = output["name"] + width = output["width"] + address = output["address"] + + read_case_statement_body += f"\t\t\tBASE_ADDR + {address}: rdata_o <= {{{16-width}'b0, {name}}}\n" + + if width == 1: + write_case_statement_body += f"\t\t\tBASE_ADDR + {address}: {name} <= wdata_i[0]\n" + else: + write_case_statement_body += f"\t\t\tBASE_ADDR + {address}: {name} <= wdata_i[{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() + + memory_handler_hdl = f""" +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 + {self.max_rel_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 = f"" + return hdl + + def hdl_top_level_ports(self): + probes = [] + + # generate inputs + for input in self.inputs: + name = input["name"] + width = input["width"] + + if width == 1: + probes.append(f"input wire {name}") + + else: + probes.append(f"input wire [{width-1}:0] {name}") + + # generate outputs + for output in self.outputs: + name = output["name"] + width = output["width"] + + if width == 1: + probes.append(f"output reg {name}") + + else: + probes.append(f"output reg [{width-1}:0] {name}") + + print(probes) + + hdl = f"" + return hdl class LUTRAMCore: def __init__(self, config, interface): @@ -663,7 +788,7 @@ Supported commands: ), "Wrong number of arguments, only a config file and output file must both be specified." manta = Manta(argv[2]) - manta.generate(argv[3]) + manta.generate_hdl(argv[3]) # run the specified core elif argv[1] == "run": diff --git a/src/manta/io_core.v b/src/manta/io_core.v index fb8dd97..651cd91 100644 --- a/src/manta/io_core.v +++ b/src/manta/io_core.v @@ -31,9 +31,15 @@ module io_core( output reg valid_o ); - parameter DEPTH = 8; parameter BASE_ADDR = 0; + initial begin + kirk = 0; + spock = 0; + uhura = 0; + chekov = 0; + end + always @(posedge clk) begin addr_o <= addr_i; wdata_o <= wdata_i; @@ -44,7 +50,7 @@ module io_core( // check if address is valid - if( (valid_i) && (addr_i >= BASE_ADDR) && (addr_i <= BASE_ADDR + 2)) begin + if( (valid_i) && (addr_i >= BASE_ADDR) && (addr_i <= BASE_ADDR + 7)) begin if(!rw_i) begin // reads case (addr_i) diff --git a/test/io_core_tb.sv b/test/io_core_tb.sv new file mode 100644 index 0000000..b5d94d8 --- /dev/null +++ b/test/io_core_tb.sv @@ -0,0 +1,114 @@ +`default_nettype none + +`define CP 10 +`define HCP 5 + +module io_core_tb; + + // boilerplate + logic clk; + integer test_num; + + // inputs + logic picard; + logic [6:0] data; + logic [9:0] laforge; + logic troi; + + // outputs + logic kirk; + logic [4:0] spock; + logic [2:0] uhura; + logic chekov; + + // tb -> io bus + logic [15:0] tb_io_addr; + logic [15:0] tb_io_wdata; + logic [15:0] tb_io_rdata; + logic tb_io_rw; + logic tb_io_valid; + + // la -> io bus + logic [15:0] la_tb_addr; + logic [15:0] la_tb_wdata; + logic [15:0] la_tb_rdata; + logic la_tb_rw; + logic la_tb_valid; + + + io_core #(.BASE_ADDR(0), .SAMPLE_DEPTH(128)) io( + .clk(clk), + + // inputs + .picard(picard), + .data(data), + .laforge(laforge), + .troi(troi), + + // outputs + .kirk(kirk), + .spock(spock), + .uhura(uhura), + .chekov(chekov), + + // input port + .addr_i(tb_io_addr), + .wdata_i(tb_io_wdata), + .rdata_i(tb_io_rdata), + .rw_i(tb_io_rw), + .valid_i(tb_io_valid), + + // output port + .addr_o(la_tb_addr), + .wdata_o(la_tb_wdata), + .rdata_o(la_tb_rdata), + .rw_o(la_tb_rw), + .valid_o(la_tb_valid)); + + always begin + #`HCP + clk = !clk; + end + + initial begin + $dumpfile("io_core_tb.vcd"); + $dumpvars(0, io_core_tb); + + // setup and reset + clk = 0; + test_num = 0; + + tb_io_addr = 0; + tb_io_rdata = 0; + tb_io_wdata = 0; + tb_io_rw = 0; + tb_io_valid = 0; + + picard = 0; + data = 0; + laforge = 0; + troi = 0; + + #`HCP + #(10*`CP); + + /* ==== Test 1 Begin ==== */ + $display("\n=== test 1: read state register ==="); + test_num = 1; + + tb_io_addr = 0; + tb_io_valid = 1; + #`CP + tb_io_valid = 0; + while (!la_tb_valid) #`CP; + $display(" -> read 0x%h from state reg (addr 0x0000)", la_tb_rdata); + + + #(10*`CP); + /* ==== Test 1 End ==== */ + + $finish(); + end +endmodule + +`default_nettype wire \ No newline at end of file