replace uart modules with zipcpu for testing, TX seems to misalign itself

This commit is contained in:
Fischer Moseley 2023-03-03 17:46:26 -05:00
parent 70e2bd10e7
commit a70ba2d0a8
4 changed files with 319 additions and 143 deletions

117
src/manta/rx_uart.v Normal file
View File

@ -0,0 +1,117 @@
////////////////////////////////////////////////////////////////////////////////
//
// 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.
//
////////////////////////////////////////////////////////////////////////////////
//
//
`default_nettype none
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

180
src/manta/tx_uart.v Normal file
View File

@ -0,0 +1,180 @@
////////////////////////////////////////////////////////////////////////////////
//
// Filename: txuart.v
//
// Project: Verilog Tutorial Example file
//
// Purpose: Transmit outputs over a single UART line. This particular UART
// implementation has been extremely simplified: it does not handle
// generating break conditions, nor does it handle anything other than the
// 8N1 (8 data bits, no parity, 1 stop bit) UART sub-protocol.
//
// To interface with this module, connect it to your system clock, and
// pass it the byte of data you wish to transmit. Strobe the i_wr line
// high for one cycle, and your data will be off. Wait until the 'o_busy'
// line is low before strobing the i_wr line again--this implementation
// has NO BUFFER, so strobing i_wr while the core is busy will just
// get ignored. The output will be placed on the o_txuart output line.
//
// There are known deficiencies in the formal proof found within this
// module. These have been left behind for you (the student) to fix.
//
// 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.
//
////////////////////////////////////////////////////////////////////////////////
//
//
`default_nettype none
//
//
//
module tx_uart(
input wire i_clk,
input wire i_wr,
input wire [7:0] i_data,
output reg o_uart_tx,
output reg o_busy);
parameter [23:0] CLOCKS_PER_BAUD = 24'd868;
// A line to tell others when we are ready to accept data. If
// (i_wr)&&(!o_busy) is ever true, then the core has accepted a byte
// for transmission.
// Define several states
localparam [3:0] START = 4'h0,
BIT_ZERO = 4'h1,
BIT_ONE = 4'h2,
BIT_TWO = 4'h3,
BIT_THREE = 4'h4,
BIT_FOUR = 4'h5,
BIT_FIVE = 4'h6,
BIT_SIX = 4'h7,
BIT_SEVEN = 4'h8,
LAST = 4'h8,
IDLE = 4'hf;
reg [23:0] counter;
reg [3:0] state;
reg [8:0] lcl_data;
reg baud_stb;
// o_busy
//
// This is a register, designed to be true is we are ever busy above.
// originally, this was going to be true if we were ever not in the
// idle state. The logic has since become more complex, hence we have
// a register dedicated to this and just copy out that registers value.
initial o_busy = 1'b0;
initial state = IDLE;
always @(posedge i_clk)
if ((i_wr)&&(!o_busy))
// Immediately start us off with a start bit
{ o_busy, state } <= { 1'b1, START };
else if (baud_stb)
begin
if (state == IDLE) // Stay in IDLE
{ o_busy, state } <= { 1'b0, IDLE };
else if (state < LAST) begin
o_busy <= 1'b1;
state <= state + 1'b1;
end else // Wait for IDLE
{ o_busy, state } <= { 1'b1, IDLE };
end
// lcl_data
//
// This is our working copy of the i_data register which we use
// when transmitting. It is only of interest during transmit, and is
// allowed to be whatever at any other time. Hence, if o_busy isn't
// true, we can always set it. On the one clock where o_busy isn't
// true and i_wr is, we set it and o_busy is true thereafter.
// Then, on any baud_stb (i.e. change between baud intervals)
// we simple logically shift the register right to grab the next bit.
initial lcl_data = 9'h1ff;
always @(posedge i_clk)
if ((i_wr)&&(!o_busy))
lcl_data <= { i_data, 1'b0 };
else if (baud_stb)
lcl_data <= { 1'b1, lcl_data[8:1] };
// o_uart_tx
//
// This is the final result/output desired of this core. It's all
// centered about o_uart_tx. This is what finally needs to follow
// the UART protocol.
//
assign o_uart_tx = lcl_data[0];
// All of the above logic is driven by the baud counter. Bits must last
// CLOCKS_PER_BAUD in length, and this baud counter is what we use to
// make certain of that.
//
// The basic logic is this: at the beginning of a bit interval, start
// the baud counter and set it to count CLOCKS_PER_BAUD. When it gets
// to zero, restart it.
//
// However, comparing a 28'bit number to zero can be rather complex--
// especially if we wish to do anything else on that same clock. For
// that reason, we create "baud_stb". baud_stb is
// nothing more than a flag that is true anytime baud_counter is zero.
// It's true when the logic (above) needs to step to the next bit.
// Simple enough?
//
// I wish we could stop there, but there are some other (ugly)
// conditions to deal with that offer exceptions to this basic logic.
//
// 1. When the user has commanded a BREAK across the line, we need to
// wait several baud intervals following the break before we start
// transmitting, to give any receiver a chance to recognize that we are
// out of the break condition, and to know that the next bit will be
// a stop bit.
//
// 2. A reset is similar to a break condition--on both we wait several
// baud intervals before allowing a start bit.
//
// 3. In the idle state, we stop our counter--so that upon a request
// to transmit when idle we can start transmitting immediately, rather
// than waiting for the end of the next (fictitious and arbitrary) baud
// interval.
//
// When (i_wr)&&(!o_busy)&&(state == IDLE) then we're not only in
// the idle state, but we also just accepted a command to start writing
// the next word. At this point, the baud counter needs to be reset
// to the number of CLOCKS_PER_BAUD, and baud_stb set to zero.
//
// The logic is a bit twisted here, in that it will only check for the
// above condition when baud_stb is false--so as to make
// certain the STOP bit is complete.
initial baud_stb = 1'b1;
initial counter = 0;
always @(posedge i_clk)
if ((i_wr)&&(!o_busy))
begin
counter <= CLOCKS_PER_BAUD - 1'b1;
baud_stb <= 1'b0;
end else if (!baud_stb)
begin
baud_stb <= (counter == 24'h01);
counter <= counter - 1'b1;
end else if (state != IDLE)
begin
counter <= CLOCKS_PER_BAUD - 1'b1;
baud_stb <= 1'b0;
end
endmodule

View File

@ -1,69 +0,0 @@
`default_nettype none
`timescale 1ns / 1ps
module uart_rx(
input wire clk,
input wire rst,
input wire rxd,
output reg [DATA_WIDTH - 1:0] axiod,
output reg axiov
);
parameter DATA_WIDTH = 0;
parameter CLK_FREQ_HZ = 0;
parameter BAUDRATE = 0;
localparam BAUD_PERIOD = CLK_FREQ_HZ / BAUDRATE;
reg [$clog2(BAUD_PERIOD) - 1:0] baud_counter;
reg [$clog2(DATA_WIDTH + 2):0] bit_index;
reg [DATA_WIDTH + 2 : 0] data_buf;
reg prev_rxd;
reg busy;
always_ff @(posedge clk) begin
prev_rxd <= rxd;
axiov <= 0;
baud_counter <= (baud_counter == BAUD_PERIOD - 1) ? 0 : baud_counter + 1;
// reset logic
if(rst) begin
bit_index <= 0;
axiod <= 0;
busy <= 0;
baud_counter <= 0;
end
// start receiving if we see a falling edge, and not already busy
else if (prev_rxd && ~rxd && ~busy) begin
busy <= 1;
data_buf <= 0;
baud_counter <= 0;
end
// if we're actually receiving
else if (busy) begin
if (baud_counter == BAUD_PERIOD / 2) begin
data_buf[bit_index] <= rxd;
bit_index <= bit_index + 1;
if (bit_index == DATA_WIDTH + 1) begin
busy <= 0;
bit_index <= 0;
if (rxd && ~data_buf[0]) begin
axiod <= data_buf[DATA_WIDTH : 1];
axiov <= 1;
end
end
end
end
end
endmodule
`default_nettype wire

View File

@ -24,23 +24,15 @@ module minimal_bus_tb;
integer test_num;
string msg;
logic [7:0] char;
logic baud_counter;
assign baud_counter = utx.baud_counter == 0;
// tb --> uart_rx signals
logic tb_urx_rxd;
uart_rx #(
.DATA_WIDTH(8),
.CLK_FREQ_HZ(100_000_000),
.BAUDRATE(10_000_000)
) urx (
.clk(clk),
.rst(rst),
.rxd(tb_urx_rxd),
.axiod(urx_brx_axid),
.axiov(urx_brx_axiv));
rx_uart #(.CLOCKS_PER_BAUD(10)) 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;
@ -65,13 +57,13 @@ module minimal_bus_tb;
logic brx_mem_valid;
lut_mem #(
.DEPTH(8),
.DEPTH(32),
.BASE_ADDR(0)
) mem (
.clk(clk),
.addr_i(brx_mem_addr),
.wdata_i(brx_mem_wdata),
.rdata_i(0),
.rdata_i(16'h0),
.rw_i(brx_mem_rw),
.valid_i(brx_mem_valid),
@ -97,45 +89,24 @@ module minimal_bus_tb;
.axiod(btx_utx_axid),
.axiov(btx_utx_axiv),
.axior(btx_utx_axir));
.axior(~btx_utx_axib));
// bridge_tx --> uart_tx signals
logic [7:0] btx_utx_axid;
logic btx_utx_axiv;
logic btx_utx_axir;
logic btx_utx_axib;
uart_tx #(
.DATA_WIDTH(8),
.CLK_FREQ_HZ(100_000_000),
.BAUDRATE(10_000_000)
) utx (
.clk(clk),
.rst(rst),
tx_uart #(.CLOCKS_PER_BAUD(10)) utx(
.i_clk(clk),
.i_wr(btx_utx_axiv),
.i_data(btx_utx_axid),
.axiid(btx_utx_axid),
.axiiv(btx_utx_axiv),
.axiir(btx_utx_axir),
.txd(utx_tb_txd));
.o_uart_tx(utx_tb_txd),
.o_busy(btx_utx_axib));
// utx --> tb signals
logic utx_tb_txd;
/*
actually just for shiggles let's see what happens when you put a uart_rx on a uart_tx
*/
uart_rx #(
.DATA_WIDTH(8),
.CLK_FREQ_HZ(100_000_000),
.BAUDRATE(10_000_000)
) tb_decoder (
.clk(clk),
.rst(rst),
.rxd(utx_tb_txd),
.axiod(),
.axiov());
always begin
#`HCP
clk = !clk;
@ -157,14 +128,8 @@ module minimal_bus_tb;
#`HCP
// throw some nonzero data in the memories just so we know that we're pulling from the right ones
mem.mem[0] = 16'h0000;
mem.mem[1] = 16'h0001;
mem.mem[2] = 16'h0002;
mem.mem[3] = 16'h0003;
mem.mem[4] = 16'h0004;
mem.mem[5] = 16'h0005;
mem.mem[6] = 16'h0006;
mem.mem[7] = 16'h0007;
for(int i=0; i< 32; i++) mem.mem[i] = i;
#(10*`CP);
@ -189,29 +154,12 @@ module minimal_bus_tb;
/* ==== Test 3 Begin ==== */
$display("\n=== test 3: read from 0x0000-0x0007 for baseline functionality ===");
test_num = 3;
msg = {"M0000", 8'h0D, 8'h0A};
`SEND_MSG_BITS(msg)
msg = {"M0001", 8'h0D, 8'h0A};
`SEND_MSG_BITS(msg)
msg = {"M0002", 8'h0D, 8'h0A};
`SEND_MSG_BITS(msg)
msg = {"M0003", 8'h0D, 8'h0A};
`SEND_MSG_BITS(msg)
msg = {"M0004", 8'h0D, 8'h0A};
`SEND_MSG_BITS(msg)
msg = {"M0005", 8'h0D, 8'h0A};
`SEND_MSG_BITS(msg)
msg = {"M0006", 8'h0D, 8'h0A};
`SEND_MSG_BITS(msg)
msg = {"M0007", 8'h0D, 8'h0A};
`SEND_MSG_BITS(msg)
for(logic[15:0] j=0; j<32; j++) begin
$display($sformatf("M%H", j));
msg = {$sformatf("M%H", j), 8'h0D, 8'h0A};
`SEND_MSG_BITS(msg)
end
#(10*`CP);
/* ==== Test 3 End ==== */