diff --git a/.gitignore b/.gitignore index 00955ad..a033f23 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ *.bit *.vcd *.out +dist/ diff --git a/examples/counter/src/debug.sv b/examples/counter/src/debug.sv index 5c62ea5..a5a60ca 100644 --- a/examples/counter/src/debug.sv +++ b/examples/counter/src/debug.sv @@ -1,612 +1,33 @@ `default_nettype none `timescale 1ns / 1ps -/* -This manta definition was autogenerated on 09 Feb 2023 at 15:26:40 by fischerm +module top_level ( + input wire clk, + input wire btnc, + input wire btnu, + input wire [15:0] sw, -If this breaks or if you've got dank formal verification memes, -please contact fischerm [at] mit.edu. -*/ + output logic [15:0] led, + input wire uart_txd_in, + output logic uart_rxd_out + ); + + // Signal Generator + logic [7:0] count; + always_ff @(posedge clk) count <= count + 1; -`define IDLE 0 -`define ARM 1 -`define FILL 2 -`define DOWNLINK 3 - -`define ARM_BYTE 8'b00110000 - -module manta ( - input wire clk, - input wire rst, - - /* Begin autogenerated probe definitions */ - input wire larry, - input wire curly, - input wire moe, - input wire [3:0] shemp, - /* End autogenerated probe definitions */ - - input wire rxd, - output logic txd); - - /* Begin autogenerated parameters */ - localparam SAMPLE_WIDTH = 7; - localparam SAMPLE_DEPTH = 4096; - - localparam DATA_WIDTH = 8; - localparam BAUDRATE = 115200; - localparam CLK_FREQ_HZ = 100000000; - - logic trigger; - assign trigger = (larry && curly && ~moe); - - logic [SAMPLE_WIDTH - 1 : 0] concat; - assign concat = {larry, curly, moe, shemp}; - /* End autogenerated parameters */ - - - // FIFO - logic [7:0] fifo_data_in; - logic fifo_input_ready; - - logic fifo_request_output; - logic [7:0] fifo_data_out; - logic fifo_output_valid; - - logic [11:0] fifo_size; - logic fifo_empty; - logic fifo_full; - - fifo #( - .WIDTH(SAMPLE_WIDTH), - .DEPTH(SAMPLE_DEPTH) - ) fifo ( + // debugger + manta manta( .clk(clk), - .rst(rst), + .rst(btnc), + .larry(count[0]), + .curly(count[1]), + .moe(count[2]), + .shemp(count[3:0]), + + .rxd(uart_txd_in), + .txd(uart_rxd_out)); - .data_in(fifo_data_in), - .input_ready(fifo_input_ready), - - .request_output(fifo_request_output), - .data_out(fifo_data_out), - .output_valid(fifo_output_valid), - - .size(fifo_size), - .empty(fifo_empty), - .full(fifo_full)); - - // Serial interface - logic tx_start; - logic [7:0] tx_data; - logic tx_busy; - - logic [7:0] rx_data; - logic rx_ready; - logic rx_busy; - - - uart_tx #( - .DATA_WIDTH(DATA_WIDTH), - .CLK_FREQ_HZ(CLK_FREQ_HZ), - .BAUDRATE(BAUDRATE)) - tx ( - .clk(clk), - .rst(rst), - .start(tx_start), - .data(tx_data), - - .busy(tx_busy), - .txd(txd)); - - uart_rx #( - .DATA_WIDTH(DATA_WIDTH), - .CLK_FREQ_HZ(CLK_FREQ_HZ), - .BAUDRATE(BAUDRATE)) - rx ( - .clk(clk), - .rst(rst), - .rxd(rxd), - - .data(rx_data), - .ready(rx_ready), - .busy(rx_busy)); - - - /* State Machine */ - /* - - IDLE: - - literally nothing is happening. the FIFO isn't being written to or read from. it should be empty. - - an arm command over serial is what brings us into the ARM state - - ARM: - - popping things onto FIFO. if the fifo is halfway full, we pop them off too. - - meeting the trigger condition is what moves us into the filing state - - FILL: - - popping things onto FIFO, until it's full. once it is full, we move into the downlinking state - - DOWNLINK: - - popping thing off of the FIFO until it's empty. once it's empty, we move back into the IDLE state - */ - - /* Downlink State Machine Controller */ - /* - - - manta enters the downlink state - - set fifo_output_request high for a clock cycle - - when fifo_output_valid goes high, send fifo_data_out across the line - - do nothing until tx_busy goes low - - goto step 2 - - */ - - logic [1:0] state; - logic [2:0] downlink_fsm_state; - - always_ff @(posedge clk) begin - if(rst) begin - state <= `IDLE; - downlink_fsm_state <= 0; - tx_data <= 0; - tx_start <= 0; - end - else begin - - case (state) - `IDLE : begin - fifo_input_ready <= 0; - fifo_request_output <= 0; - - if (rx_ready && rx_data == `ARM_BYTE) state <= `ARM; - - end - - `ARM : begin - // place samples into FIFO - fifo_input_ready <= 1; - fifo_data_in <= concat; - - // remove old samples if we're more than halfway full - fifo_request_output <= (fifo_size >= SAMPLE_DEPTH / 2); - - if(trigger) state <= `FILL; - end - - `FILL : begin - // place samples into FIFO - fifo_input_ready <= 1; - fifo_data_in <= concat; - - // don't pop anything out the FIFO - fifo_request_output <= 0; - - if(fifo_size == SAMPLE_DEPTH - 1) state <= `DOWNLINK; - end - - `DOWNLINK : begin - // place no samples into FIFO - fifo_input_ready <= 0; - - - case (downlink_fsm_state) - 0 : begin - if (~fifo_empty) begin - fifo_request_output <= 1; - downlink_fsm_state <= 1; - end - - else state <= `IDLE; - end - - 1 : begin - fifo_request_output <= 0; - - if (fifo_output_valid) begin - tx_data <= fifo_data_out; - tx_start <= 1; - downlink_fsm_state <= 2; - end - end - - 2 : begin - tx_start <= 0; - - if (~tx_busy && ~tx_start) downlink_fsm_state <= 0; - end - endcase - - end - endcase - end - end - -endmodule - - -`default_nettype wire`default_nettype none -`timescale 1ns / 1ps - -module fifo ( - input wire clk, - input wire rst, - - input wire [WIDTH - 1:0] data_in, - input wire input_ready, - - input wire request_output, - output logic [WIDTH - 1:0] data_out, - output logic output_valid, - - output logic [AW:0] size, - output logic empty, - output logic full - ); - - parameter WIDTH = 8; - parameter DEPTH = 4096; - localparam AW = $clog2(DEPTH); - - logic [AW:0] write_pointer; - logic [AW:0] read_pointer; - - logic empty_int; - assign empty_int = (write_pointer[AW] == read_pointer[AW]); - - logic full_or_empty; - assign full_or_empty = (write_pointer[AW-1:0] == read_pointer[AW-1:0]); - - assign full = full_or_empty & !empty_int; - assign empty = full_or_empty & empty_int; - assign size = write_pointer - read_pointer; - - logic output_valid_pip_0; - logic output_valid_pip_1; - - always @(posedge clk) begin - if (input_ready && ~full) - write_pointer <= write_pointer + 1'd1; - - if (request_output && ~empty) - read_pointer <= read_pointer + 1'd1; - output_valid_pip_0 <= request_output; - output_valid_pip_1 <= output_valid_pip_0; - output_valid <= output_valid_pip_1; - - if (rst) begin - read_pointer <= 0; - write_pointer <= 0; - end - end - - xilinx_true_dual_port_read_first_2_clock_ram #( - .RAM_WIDTH(WIDTH), - .RAM_DEPTH(DEPTH), - .RAM_PERFORMANCE("HIGH_PERFORMANCE") - - ) buffer ( - - // write port - .clka(clk), - .rsta(rst), - .ena(1), - .addra(write_pointer), - .dina(data_in), - .wea(input_ready), - .regcea(1), - .douta(), - - // read port - .clkb(clk), - .rstb(rst), - .enb(1), - .addrb(read_pointer), - .dinb(), - .web(0), - .regceb(1), - .doutb(data_out)); - endmodule - -`default_nettype wire -`default_nettype none -`timescale 1ns / 1ps - - -module uart_tx( - input wire clk, - input wire rst, - input wire [DATA_WIDTH-1:0] data, - input wire start, - - output logic busy, - output logic txd - ); - - // Just going to stick to 8N1 for now, we'll come back and - // parameterize this later. - - parameter DATA_WIDTH = 8; - parameter CLK_FREQ_HZ = 100_000_000; - parameter BAUDRATE = 115200; - - localparam PRESCALER = CLK_FREQ_HZ / BAUDRATE; - - logic [$clog2(PRESCALER) - 1:0] baud_counter; - logic [$clog2(DATA_WIDTH + 2):0] bit_index; - logic [DATA_WIDTH - 1:0] data_buf; - - // make secondary logic for baudrate - always_ff @(posedge clk) begin - if(rst) baud_counter <= 0; - else begin - baud_counter <= (baud_counter == PRESCALER - 1) ? 0 : baud_counter + 1; - end - end - - always_ff @(posedge clk) begin - - // reset logic - if(rst) begin - bit_index <= 0; - busy <= 0; - txd <= 1; // idle high - end - - // enter transmitting state logic - // don't allow new requests to interrupt current - // transfers - if(start && ~busy) begin - busy <= 1; - data_buf <= data; - end - - - // transmitting state logic - else if(baud_counter == 0 && busy) begin - - if (bit_index == 0) begin - txd <= 0; - bit_index <= bit_index + 1; - end - - else if ((bit_index < DATA_WIDTH + 1) && (bit_index > 0)) begin - txd <= data_buf[bit_index - 1]; - bit_index <= bit_index + 1; - end - - else if (bit_index == DATA_WIDTH + 1) begin - txd <= 1; - bit_index <= bit_index + 1; - end - - else if (bit_index >= DATA_WIDTH + 1) begin - busy <= 0; - bit_index <= 0; - end - end - end -endmodule - - -`default_nettype wire - -// Xilinx True Dual Port RAM, Read First, Dual Clock -// This code implements a parameterizable true dual port memory (both ports can read and write). -// The behavior of this RAM is when data is written, the prior memory contents at the write -// address are presented on the output port. If the output data is -// not needed during writes or the last read value is desired to be retained, -// it is suggested to use a no change RAM as it is more power efficient. -// If a reset or enable is not necessary, it may be tied off or removed from the code. - -module xilinx_true_dual_port_read_first_2_clock_ram #( - parameter RAM_WIDTH = 18, // Specify RAM data width - parameter RAM_DEPTH = 1024, // Specify RAM depth (number of entries) - parameter RAM_PERFORMANCE = "HIGH_PERFORMANCE", // Select "HIGH_PERFORMANCE" or "LOW_LATENCY" - parameter INIT_FILE = "" // Specify name/location of RAM initialization file if using one (leave blank if not) -) ( - input [clogb2(RAM_DEPTH-1)-1:0] addra, // Port A address bus, width determined from RAM_DEPTH - input [clogb2(RAM_DEPTH-1)-1:0] addrb, // Port B address bus, width determined from RAM_DEPTH - input [RAM_WIDTH-1:0] dina, // Port A RAM input data - input [RAM_WIDTH-1:0] dinb, // Port B RAM input data - input clka, // Port A clock - input clkb, // Port B clock - input wea, // Port A write enable - input web, // Port B write enable - input ena, // Port A RAM Enable, for additional power savings, disable port when not in use - input enb, // Port B RAM Enable, for additional power savings, disable port when not in use - input rsta, // Port A output reset (does not affect memory contents) - input rstb, // Port B output reset (does not affect memory contents) - input regcea, // Port A output register enable - input regceb, // Port B output register enable - output [RAM_WIDTH-1:0] douta, // Port A RAM output data - output [RAM_WIDTH-1:0] doutb // Port B RAM output data -); - - reg [RAM_WIDTH-1:0] BRAM [RAM_DEPTH-1:0]; - reg [RAM_WIDTH-1:0] ram_data_a = {RAM_WIDTH{1'b0}}; - reg [RAM_WIDTH-1:0] ram_data_b = {RAM_WIDTH{1'b0}}; - - //this loop below allows for rendering with iverilog simulations! - /* - integer idx; - for(idx = 0; idx < RAM_DEPTH; idx = idx+1) begin: cats - wire [RAM_WIDTH-1:0] tmp; - assign tmp = BRAM[idx]; - end - */ - - // The following code either initializes the memory values to a specified file or to all zeros to match hardware - generate - if (INIT_FILE != "") begin: use_init_file - initial - $readmemh(INIT_FILE, BRAM, 0, RAM_DEPTH-1); - end else begin: init_bram_to_zero - integer ram_index; - initial - for (ram_index = 0; ram_index < RAM_DEPTH; ram_index = ram_index + 1) - BRAM[ram_index] = {RAM_WIDTH{1'b0}}; - end - endgenerate - integer idx; - // initial begin - // for (idx = 0; idx < RAM_DEPTH; idx = idx + 1) begin - // $dumpvars(0, BRAM[idx]); - // end - // end - always @(posedge clka) - if (ena) begin - if (wea) - BRAM[addra] <= dina; - ram_data_a <= BRAM[addra]; - end - - always @(posedge clkb) - if (enb) begin - if (web) - BRAM[addrb] <= dinb; - ram_data_b <= BRAM[addrb]; - end - - // The following code generates HIGH_PERFORMANCE (use output register) or LOW_LATENCY (no output register) - generate - if (RAM_PERFORMANCE == "LOW_LATENCY") begin: no_output_register - - // The following is a 1 clock cycle read latency at the cost of a longer clock-to-out timing - assign douta = ram_data_a; - assign doutb = ram_data_b; - - end else begin: output_register - - // The following is a 2 clock cycle read latency with improve clock-to-out timing - - reg [RAM_WIDTH-1:0] douta_reg = {RAM_WIDTH{1'b0}}; - reg [RAM_WIDTH-1:0] doutb_reg = {RAM_WIDTH{1'b0}}; - - always @(posedge clka) - if (rsta) - douta_reg <= {RAM_WIDTH{1'b0}}; - else if (regcea) - douta_reg <= ram_data_a; - - always @(posedge clkb) - if (rstb) - doutb_reg <= {RAM_WIDTH{1'b0}}; - else if (regceb) - doutb_reg <= ram_data_b; - - assign douta = douta_reg; - assign doutb = doutb_reg; - - end - endgenerate - - // The following function calculates the address width based on specified RAM depth - function integer clogb2; - input integer depth; - for (clogb2=0; depth>0; clogb2=clogb2+1) - depth = depth >> 1; - endfunction - -endmodule - -// The following is an instantiation template for xilinx_true_dual_port_read_first_2_clock_ram -/* - // Xilinx True Dual Port RAM, Read First, Dual Clock - xilinx_true_dual_port_read_first_2_clock_ram #( - .RAM_WIDTH(18), // Specify RAM data width - .RAM_DEPTH(1024), // Specify RAM depth (number of entries) - .RAM_PERFORMANCE("HIGH_PERFORMANCE"), // Select "HIGH_PERFORMANCE" or "LOW_LATENCY" - .INIT_FILE("") // Specify name/location of RAM initialization file if using one (leave blank if not) - ) your_instance_name ( - .addra(addra), // Port A address bus, width determined from RAM_DEPTH - .addrb(addrb), // Port B address bus, width determined from RAM_DEPTH - .dina(dina), // Port A RAM input data, width determined from RAM_WIDTH - .dinb(dinb), // Port B RAM input data, width determined from RAM_WIDTH - .clka(clka), // Port A clock - .clkb(clkb), // Port B clock - .wea(wea), // Port A write enable - .web(web), // Port B write enable - .ena(ena), // Port A RAM Enable, for additional power savings, disable port when not in use - .enb(enb), // Port B RAM Enable, for additional power savings, disable port when not in use - .rsta(rsta), // Port A output reset (does not affect memory contents) - .rstb(rstb), // Port B output reset (does not affect memory contents) - .regcea(regcea), // Port A output register enable - .regceb(regceb), // Port B output register enable - .douta(douta), // Port A RAM output data, width determined from RAM_WIDTH - .doutb(doutb) // Port B RAM output data, width determined from RAM_WIDTH - ); -*/ - - -`default_nettype none -`timescale 1ns / 1ps - -module uart_rx( - input wire clk, - input wire rst, - input wire rxd, - - output logic [DATA_WIDTH - 1:0] data, - output logic ready, - output logic busy - ); - - // Just going to stick to 8N1 for now, we'll come back and - // parameterize this later. - - parameter DATA_WIDTH = 8; - parameter CLK_FREQ_HZ = 100_000_000; - parameter BAUDRATE = 115200; - - localparam PRESCALER = CLK_FREQ_HZ / BAUDRATE; - - logic [$clog2(PRESCALER) - 1:0] baud_counter; - logic [$clog2(DATA_WIDTH + 2):0] bit_index; - logic [DATA_WIDTH + 2 : 0] data_buf; - - logic prev_rxd; - - always_ff @(posedge clk) begin - prev_rxd <= rxd; - ready <= 0; - baud_counter <= (baud_counter == PRESCALER - 1) ? 0 : baud_counter + 1; - - // reset logic - if(rst) begin - bit_index <= 0; - data <= 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 == PRESCALER / 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 - data <= data_buf[DATA_WIDTH : 1]; - ready <= 1; - end - end - end - end - end - - endmodule `default_nettype wire \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..0697d7c --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,35 @@ +[build-system] +requires = ["setuptools"] +build-backend = "setuptools.build_meta" + +[project] +name = "manta" +version = "0.0.0" +authors = [ + { name="Fischer Moseley", email="fischerm@mit.edu" }, +] +description = "An In-Situ Debugging Tool for Programmable Hardware" +readme = "README.md" +dependencies = [ + 'PyYAML', + 'pyserial', + 'pyvcd', +] + +requires-python = ">=3.7" +classifiers = [ + "Programming Language :: Python :: 3", + "License :: OSI Approved :: GNU General Public License v3 (GPLv3)", + "Operating System :: OS Independent", + "Development Status :: 2 - Pre-Alpha", +] + +[project.urls] +"Homepage" = "https://github.com/fischermoseley/manta" +"Bug Tracker" = "https://github.com/fischermoseley/manta/issues" + +[tool.setuptools.packages.find] +where = ["src"] + +[tool.setuptools.package-data] +mypkg = ["*.sv", "*.v"] \ No newline at end of file diff --git a/src/__init__.py b/src/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/fifo.sv b/src/hdl/fifo.sv similarity index 100% rename from src/fifo.sv rename to src/hdl/fifo.sv diff --git a/src/manta_template.sv b/src/hdl/manta_template.sv similarity index 100% rename from src/manta_template.sv rename to src/hdl/manta_template.sv diff --git a/src/uart_rx.sv b/src/hdl/uart_rx.sv similarity index 100% rename from src/uart_rx.sv rename to src/hdl/uart_rx.sv diff --git a/src/uart_tx.sv b/src/hdl/uart_tx.sv similarity index 100% rename from src/uart_tx.sv rename to src/hdl/uart_tx.sv diff --git a/src/hdl/uplink.sv b/src/hdl/uplink.sv new file mode 100644 index 0000000..276c211 --- /dev/null +++ b/src/hdl/uplink.sv @@ -0,0 +1,96 @@ +`default_nettype none + +`define IDLE 0 +`define RUN 1 + +module uplink( + input wire clk, + input wire rst, + input wire start, + output wire busy, + + /* Begin autogenerated probe definitions */ + output wire alice, + output wire [2:0] bob, + output wire charlotte, + output wire [3:0] donovan + /* End autogenerated probe definitions */ + ); + + /* + this works in a very simple way - all it does is chill + in the idle state, until a request to start uplinking is + received. when that happens, it'll start dumping things + from the port until the BRAM is empty, and then that's it. + + this dumping happens with the trigger condition - the clock + cycle that the triggr goes high on, we should output data. + or have the option to, with some kind of holdoff. + + oh wait this might be a little hard since we've got the two + clock cycles of latency on the BRAM, so we actually need to + preload the first two values of the bram into registers so + when it's time to go + + actually it's probably worth thinking more about how useful + this would acatully be, because right now i'm not seeing too + many situations where i'd want to use this. and we can always + come back to it + */ + + parameter WIDTH = 0; + parameter DEPTH = 0; + localparam AW = $clog2(DEPTH); + + logic [AW:0] read_pointer; + logic state; + + always_ff @(posedge clk) begin + if(rst) begin + state <= `IDLE + read_pointer <= 0; + end + + else begin + if(state == `IDLE) begin + // do nothing, just wait for trigger condition + if(start) state <= `RUN; + end + + if(state == `RUN) begin + + end + end + end + + xilinx_true_dual_port_read_first_2_clock_ram #( + .RAM_WIDTH(), + .RAM_DEPTH(), + .RAM_PERFORMANCE("HIGH PERFORMANCE") + + ) buffer ( + + // write port (currently unused) + .clka(clk), + .rsta(rst), + .ena(1), + .addra(0), + .dina(0), + .wea(0), + .regcea(1), + .douta(), + + // read port + .clkb(clk), + .rstb(rst), + .enb(1), + .addrb(read_pointer), + .dinb(), + .web(0), + .regceb(1), + .doutb(data_out)); + + +endmodule + +`default_nettype wire \ No newline at end of file diff --git a/src/xilinx_true_dual_port_read_first_2_clock_ram.v b/src/hdl/xilinx_true_dual_port_read_first_2_clock_ram.v similarity index 100% rename from src/xilinx_true_dual_port_read_first_2_clock_ram.v rename to src/hdl/xilinx_true_dual_port_read_first_2_clock_ram.v diff --git a/manta.py b/src/manta.py similarity index 95% rename from manta.py rename to src/manta.py index 091343e..5b527a4 100644 --- a/manta.py +++ b/src/manta.py @@ -4,6 +4,7 @@ import json import yaml from datetime import datetime import serial +import pkgutil debug = True @@ -12,26 +13,16 @@ version = "0.0.0" def load_source_files(path): """concatenates the contents of the list of files provided into a single string""" - source_files = [ - f for f in os.listdir(path) if os.path.isfile(os.path.join(path, f)) - ] - source_files = [f for f in source_files if f.split(".")[-1] in ["sv", "v"]] - # bring manta_template.sv to the top, if it exists - if "manta_template.sv" in source_files: - source_files.insert( - 0, source_files.pop(source_files.index("manta_template.sv")) - ) + downlink_template = pkgutil.get_data(__name__, "hdl/manta_template.sv") + downlink_template += pkgutil.get_data(__name__, "hdl/fifo.sv") + downlink_template += pkgutil.get_data(__name__, "hdl/uart_tx.sv") + downlink_template += pkgutil.get_data(__name__, "hdl/uart_rx.sv") + downlink_template += pkgutil.get_data( + __name__, "hdl/xilinx_true_dual_port_read_first_2_clock_ram.v" + ) - buf = "" - for source_file in source_files: - with open(path + source_file, "r") as f: - buf += f.read() - - return buf - - -downlink_template = load_source_files("src/") + return downlink_template def load_config(path):