add uart_rx and refactor uart_tx and bridge_tx

This commit is contained in:
Fischer Moseley 2023-07-09 18:19:49 -07:00
parent d67ac9c799
commit 38f7ee86fa
10 changed files with 306 additions and 143 deletions

5
.gitignore vendored
View File

@ -23,4 +23,7 @@ manta.v
# Python Packaging output products
dist/
*.egg-info
__pycache__/
__pycache__/
# Verilator outputs
obj_dir/

View File

@ -4,11 +4,11 @@
module bridge_rx (
input wire clk,
input wire[7:0] rx_data,
input wire rx_valid,
input wire [7:0] data_i,
input wire valid_i,
output reg[15:0] addr_o,
output reg[15:0] wdata_o,
output reg [15:0] addr_o,
output reg [15:0] wdata_o,
output reg rw_o,
output reg valid_o);
@ -40,32 +40,32 @@ module bridge_rx (
state = ACQUIRE;
end
reg [3:0] rx_data_decoded;
reg rx_data_is_0_thru_9;
reg rx_data_is_A_thru_F;
reg [3:0] data_i_decoded;
reg data_i_is_0_thru_9;
reg data_i_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);
data_i_is_0_thru_9 = (data_i >= 8'h30) & (data_i <= 8'h39);
data_i_is_A_thru_F = (data_i >= 8'h41) & (data_i <= 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;
if (data_i_is_0_thru_9) data_i_decoded = data_i - 8'h30;
else if (data_i_is_A_thru_F) data_i_decoded = data_i - 8'h41 + 'd10;
else data_i_decoded = 0;
end
always @(posedge clk) begin
if (state == ACQUIRE) begin
if(rx_valid) begin
if(valid_i) begin
if (bytes_received == 0) begin
if(rx_data == PREAMBLE) bytes_received <= 1;
if(data_i == 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;
if(data_i_is_0_thru_9 | data_i_is_A_thru_F) begin
addr_o <= (addr_o << 4) | data_i_decoded;
bytes_received <= bytes_received + 1;
end
@ -73,16 +73,16 @@ module bridge_rx (
end
else if( bytes_received == 5) begin
if( (rx_data == CR) | (rx_data == LF)) begin
if( (data_i == CR) | (data_i == LF)) begin
valid_o <= 1;
rw_o = 0;
rw_o <= 0;
bytes_received <= 0;
state <= TRANSMIT;
end
else if (rx_data_is_0_thru_9 | rx_data_is_A_thru_F) begin
else if (data_i_is_0_thru_9 | data_i_is_A_thru_F) begin
bytes_received <= bytes_received + 1;
wdata_o <= (wdata_o << 4) | rx_data_decoded;
wdata_o <= (wdata_o << 4) | data_i_decoded;
end
else state <= ERROR;
@ -90,8 +90,8 @@ module bridge_rx (
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;
if (data_i_is_0_thru_9 | data_i_is_A_thru_F) begin
wdata_o <= (wdata_o << 4) | data_i_decoded;
bytes_received <= bytes_received + 1;
end
@ -100,7 +100,7 @@ module bridge_rx (
else if (bytes_received == 9) begin
bytes_received <= 0;
if( (rx_data == CR) | (rx_data == LF)) begin
if( (data_i == CR) | (data_i == LF)) begin
valid_o <= 1;
rw_o <= 1;
state <= TRANSMIT;
@ -118,8 +118,8 @@ module bridge_rx (
state <= ACQUIRE;
end
if(rx_valid) begin
if ( (rx_data != CR) & (rx_data != LF)) begin
if(valid_i) begin
if ( (data_i != CR) & (data_i != LF)) begin
valid_o <= 0;
state <= ERROR;
end

View File

@ -1,67 +1,66 @@
`default_nettype none
`timescale 1ns/1ps
function [7:0] ascii_hex;
// convert a number from 0-15 into the corresponding ascii char
input [3:0] n;
ascii_hex = (n > 10) ? (n + 8'h30) : (n + 8'h41 - 'd10);
endfunction
module bridge_tx (
input wire clk,
input wire [15:0] rdata_i,
input wire [15:0] data_i,
input wire rw_i,
input wire valid_i,
output reg [7:0] data_o,
input wire ready_i,
output reg valid_o);
output reg start_o,
input wire done_i);
localparam PREAMBLE = 8'h4D;
localparam CR = 8'h0D;
localparam LF = 8'h0A;
logic busy;
logic [15:0] buffer;
logic [3:0] byte_counter;
reg busy = 0;
reg [15:0] buffer = 0;
reg [3:0] count = 0;
initial begin
busy = 0;
buffer = 0;
byte_counter = 0;
valid_o = 0;
end
assign start_o = busy;
always @(posedge clk) begin
// idle until valid read transaction arrives on bus
if (!busy) begin
if (valid_i && !rw_i) begin
busy <= 1;
buffer <= rdata_i;
byte_counter <= 0;
valid_o <= 1;
buffer <= data_i;
end
end
if (busy) begin
// uart module is done transmitting a byte
if(done_i) begin
count <= count + 1;
if(ready_i) begin
byte_counter <= byte_counter + 1;
// message has been transmitted
if (count > 5) begin
count <= 0;
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
// go back to idle or transmit next message
if (valid_i && !rw_i) buffer <= data_i;
else busy <= 0;
end
end
end
end
always @(*) begin
case (byte_counter)
case (count)
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);
1: data_o = ascii_hex(buffer[15:12]);
2: data_o = ascii_hex(buffer[11:8]);
3: data_o = ascii_hex(buffer[7:4]);
4: data_o = ascii_hex(buffer[3:0]);
5: data_o = CR;
6: data_o = LF;
default: data_o = 0;

View File

@ -0,0 +1,70 @@
`default_nettype none
`timescale 1ns/1ps
module uart_rx (
input wire clk,
input wire rx,
output reg [7:0] data_o,
output reg valid_o);
parameter CLOCKS_PER_BAUD = 0;
initial data_o = 0;
initial valid_o = 0;
reg [$clog2(CLOCKS_PER_BAUD)-1:0] baud_counter = 0;
reg [7:0] buffer = 0;
reg [3:0] bit_index = 0;
reg prev_rx = 1;
reg busy = 0;
always @(posedge clk) begin
prev_rx <= rx;
valid_o <= 0;
if (!busy) begin
if (prev_rx && !rx) begin
busy <= 1;
end
end
else begin
// run baud counter
baud_counter <= (baud_counter < CLOCKS_PER_BAUD-1) ? baud_counter + 1 : 0;
// sample rx in the middle of a baud period
if (baud_counter == (CLOCKS_PER_BAUD/2) - 2) begin
// fill buffer until end of byte on the wire
if(bit_index <= 8) begin
buffer <= {rx, buffer[7:1]};
bit_index = bit_index + 1;
end
else begin
// reset system state
busy <= 0;
baud_counter <= 0;
bit_index <= 0;
// output word if stop bit received
if(rx) begin
data_o <= buffer;
valid_o <= 1;
end
end
end
end
end
endmodule
`default_nettype wire

View File

@ -1,17 +1,18 @@
rx_uart #(.CLOCKS_PER_BAUD(/* CLOCKS_PER_BAUD */)) urx (
.i_clk(clk),
.i_uart_rx(rx),
.o_wr(urx_brx_axiv),
.o_data(urx_brx_axid));
uart_rx #(.CLOCKS_PER_BAUD(/* CLOCKS_PER_BAUD */)) urx (
.clk(clk),
.rx(rx),
reg [7:0] urx_brx_axid;
reg urx_brx_axiv;
.data_o(urx_brx_data),
.valid_o(urx_brx_valid));
reg [7:0] urx_brx_data;
reg urx_brx_valid;
bridge_rx brx (
.clk(clk),
.rx_data(urx_brx_axid),
.rx_valid(urx_brx_axiv),
.data_i(urx_brx_data),
.valid_i(urx_brx_valid),
.addr_o(),
.wdata_o(),

View File

@ -4,65 +4,54 @@
module uart_tx (
input wire clk,
input wire [7:0] data,
input wire valid,
output reg busy,
output reg ready,
input wire [7:0] data_i,
input wire start_i,
output reg done_o,
output reg tx);
// this transmitter only works with 8N1 serial, at configurable baudrate
parameter CLOCKS_PER_BAUD = 868;
// this module supports only 8N1 serial at a configurable baudrate
parameter CLOCKS_PER_BAUD = 0;
reg [$clog2(CLOCKS_PER_BAUD)-1:0] baud_counter = 0;
reg [9:0] baud_counter;
reg [8:0] data_buf;
reg [3:0] bit_index;
reg [8:0] buffer = 0;
reg [3:0] bit_index = 0;
initial begin
baud_counter = CLOCKS_PER_BAUD;
data_buf = 0;
bit_index = 0;
busy = 0;
ready = 1;
tx = 1;
end
initial done_o = 1;
initial tx = 1;
always @(posedge clk) begin
if (valid && !busy) begin
data_buf <= {1'b1, data};
bit_index <= 0;
tx <= 0; //wafflestomp that start bit
if (start_i && done_o) begin
baud_counter <= CLOCKS_PER_BAUD - 1;
busy <= 1;
ready <= 0;
buffer <= {1'b1, data_i};
bit_index <= 0;
done_o <= 0;
tx <= 0;
end
else if (busy) begin
else if (!done_o) begin
baud_counter <= baud_counter - 1;
done_o <= (baud_counter == 1) && (bit_index == 9);
ready <= (baud_counter == 1) && (bit_index == 9);
// a baud period has elapsed
if (baud_counter == 0) begin
baud_counter <= CLOCKS_PER_BAUD - 1;
// clock out another bit if there are any left
if (bit_index < 9) begin
tx <= buffer[bit_index];
bit_index <= bit_index + 1;
end
if (bit_index == 9) begin
if(valid) begin
data_buf <= {1'b1, data};
// byte has been sent, send out next one or go to idle
else begin
if(start_i) begin
buffer <= {1'b1, data_i};
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;
else done_o <= 1;
end
end
end

View File

@ -1,23 +1,23 @@
bridge_tx btx (
.clk(clk),
.rdata_i(),
.data_i(),
.rw_i(),
.valid_i(),
.ready_i(utx_btx_ready),
.data_o(btx_utx_data),
.valid_o(btx_utx_valid));
.start_o(btx_utx_start),
.done_i(utx_btx_done));
logic utx_btx_ready;
logic btx_utx_valid;
logic [7:0] btx_utx_data;
reg [7:0] btx_utx_data;
reg btx_utx_start;
reg utx_btx_done;
uart_tx #(.CLOCKS_PER_BAUD(/* CLOCKS_PER_BAUD */)) utx (
.clk(clk),
.data(btx_utx_data),
.valid(btx_utx_valid),
.ready(utx_btx_ready),
.data_i(btx_utx_data),
.start_i(btx_utx_start),
.done_o(utx_btx_done),
.tx(tx));

View File

@ -4,38 +4,50 @@
module uart_tb();
logic clk;
logic rst;
logic [7:0] tx_data, rx_data;
logic tx_start, rx_ready;
logic tx_busy, rx_busy;
logic txd;
logic [7:0] tx_data;
logic tx_start;
uart_tx #(
.DATA_WIDTH(8),
.CLK_FREQ_HZ(100_000_000),
.BAUDRATE(115200))
tx (
// transmitters
logic tx_done_manta;
logic txd_manta;
uart_tx #(.CLOCKS_PER_BAUD(10)) tx_manta (
.clk(clk),
.rst(rst),
.data(tx_data),
.start(tx_start),
.busy(tx_busy),
.txd(txd));
.data_i(tx_data),
.start_i(tx_start),
.done_o(tx_done_manta),
.tx(txd_manta));
uart_rx #(
.DATA_WIDTH(8),
.CLK_FREQ_HZ(100_000_000),
.BAUDRATE(115200))
rx (
logic tx_busy_zipcpu;
logic tx_done_zipcpu;
logic txd_zipcpu;
assign tx_done_zipcpu = ~tx_busy_zipcpu;
tx_uart #(.CLOCKS_PER_BAUD(10)) tx_zipcpu (
.i_clk(clk),
.i_wr(tx_start),
.i_data(tx_data),
.o_uart_tx(txd_zipcpu),
.o_busy(tx_busy_zipcpu));
// receivers
logic [7:0] rx_data_manta;
logic rx_valid_manta;
uart_rx #(.CLOCKS_PER_BAUD(10)) rx_manta (
.clk(clk),
.rst(rst),
.rxd(txd),
.rx(txd_manta),
.data_o(rx_data_manta),
.valid_o(rx_valid_manta));
.data(rx_data),
.ready(rx_ready),
.busy(rx_busy));
logic [7:0] rx_data_zipcpu;
logic rx_valid_zipcpu;
rx_uart #(.CLOCKS_PER_BAUD(10)) rx_zipcpu (
.i_clk(clk),
.i_uart_rx(txd_zipcpu),
.o_wr(rx_valid_zipcpu),
.o_data(rx_data_zipcpu));
always begin
#5;
@ -46,8 +58,8 @@ module uart_tb();
$dumpfile("uart.vcd");
$dumpvars(0, uart_tb);
clk = 0;
rst = 1;
tx_data = 'h0F;
tx_data = 'hFF;
tx_start = 0;
#10;
rst = 0;
@ -55,14 +67,14 @@ module uart_tb();
tx_start = 1;
#10;
tx_start = 0;
#150000;
#10000;
// send another byte!
tx_data = 'hBE;
tx_data = 'b0100_1101;
tx_start = 1;
#10;
#3000;
tx_start = 0;
#150000;
#10000;
$finish();
end

View File

@ -0,0 +1,11 @@
---
cores:
mem:
type: block_mem
width: 18
depth: 256
uart:
port: "auto"
baudrate: 115200
clock_freq: 100000000

View File

@ -0,0 +1,78 @@
#include <stdlib.h>
#include <iostream>
#include <string>
#include <verilated.h>
#include <verilated_vcd_c.h>
#include "Vmanta.h"
vluint64_t sim_time = 0;
int main(int argc, char** argv, char** env) {
Vmanta *dut = new Vmanta;
Verilated::traceEverOn(true);
VerilatedVcdC *m_trace = new VerilatedVcdC;
dut->trace(m_trace, 5);
m_trace->open("waveform.vcd");
while(true) {
// get line from stdin
std::string line;
std::getline(std::cin, line);
for (int i=0; i < line.length(); i++) {
// advance simulation
dut->clk ^= 1;
dut->mem_clk ^= 1;
dut->eval();
m_trace->dump(sim_time);
sim_time++;
// feed it to the serial port
dut->urx_brx_axiv = 1;
dut->urx_brx_axid = line[i];
if (line[i] == 'C')
dut->urx_brx_axid = '\r';
if (line[i] == 'L')
dut->urx_brx_axid = '\n';
// advance simulation
dut->clk ^= 1;
dut->mem_clk ^= 1;
dut->eval();
m_trace->dump(sim_time);
sim_time++;
}
for (int i=0; i < 30; i++) {
// advance simulation
dut->clk ^= 1;
dut->mem_clk ^= 1;
dut->eval();
m_trace->dump(sim_time);
sim_time++;
// print whatever's on the port
if(dut->btx_utx_start)
std::cout << dut->btx_utx_data;
// advance simulation
dut->clk ^= 1;
dut->mem_clk ^= 1;
dut->eval();
m_trace->dump(sim_time);
sim_time++;
}
}
m_trace->close();
delete dut;
exit(EXIT_SUCCESS);
}