add module definitions to generated hdl

This commit is contained in:
Fischer Moseley 2023-03-08 21:06:56 -05:00
parent 7d98988b87
commit 9dba38925b
5 changed files with 599 additions and 36 deletions

471
src/inspecto_time.sv Normal file
View File

@ -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

View File

@ -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

0
src/manta/la_template.v Normal file
View File

View File

@ -1,5 +1,5 @@
`default_nettype none
`timescale 1ns / 1ps
`timescale 1ns/1ps
module uart_tx(
input wire clk,

4
src/test.py Normal file
View File

@ -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())