From 1b27a6f948145451d5c7c19c1acd880cc1533fe4 Mon Sep 17 00:00:00 2001 From: Fischer Moseley <42497969+fischermoseley@users.noreply.github.com> Date: Sun, 2 Apr 2023 23:16:25 -0400 Subject: [PATCH] add working icestick io core example --- examples/icestick/counter/build.sh | 4 - examples/icestick/counter/manta.yaml | 21 - examples/icestick/counter/src/debug.sv | 600 ------------------ examples/icestick/counter/src/top_level.sv | 33 - examples/icestick/io_core/build.sh | 6 + examples/icestick/io_core/manta.yaml | 16 + .../{counter => io_core}/pcf/top_level.pcf | 0 examples/icestick/io_core/run_io_core.py | 45 ++ examples/icestick/io_core/src/manta.v | 580 +++++++++++++++++ examples/icestick/io_core/src/top_level.sv | 32 + examples/icestick/io_core/top_level.bin | Bin 0 -> 32220 bytes 11 files changed, 679 insertions(+), 658 deletions(-) delete mode 100755 examples/icestick/counter/build.sh delete mode 100644 examples/icestick/counter/manta.yaml delete mode 100644 examples/icestick/counter/src/debug.sv delete mode 100644 examples/icestick/counter/src/top_level.sv create mode 100755 examples/icestick/io_core/build.sh create mode 100644 examples/icestick/io_core/manta.yaml rename examples/icestick/{counter => io_core}/pcf/top_level.pcf (100%) create mode 100644 examples/icestick/io_core/run_io_core.py create mode 100644 examples/icestick/io_core/src/manta.v create mode 100644 examples/icestick/io_core/src/top_level.sv create mode 100644 examples/icestick/io_core/top_level.bin diff --git a/examples/icestick/counter/build.sh b/examples/icestick/counter/build.sh deleted file mode 100755 index 3dcbf93..0000000 --- a/examples/icestick/counter/build.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash -yosys -p 'synth_ice40 -top top_level -json counter.json' src/top_level.sv -nextpnr-ice40 --hx1k --json counter.json --pcf pcf/top_level.pcf --asc counter.asc -icepack counter.asc counter.bin \ No newline at end of file diff --git a/examples/icestick/counter/manta.yaml b/examples/icestick/counter/manta.yaml deleted file mode 100644 index 88df24f..0000000 --- a/examples/icestick/counter/manta.yaml +++ /dev/null @@ -1,21 +0,0 @@ ---- -downlink: - sample_depth: 4096 - clock_freq: 12000000 - - probes: - larry: 1 - curly: 1 - moe: 1 - shemp: 4 - - triggers: - - larry && curly && ~moe - -uart: - baudrate: 115200 - port: "/dev/tty.usbserial-2102926963071" - data: 8 - parity: none - stop: 1 - timeout: 1 \ No newline at end of file diff --git a/examples/icestick/counter/src/debug.sv b/examples/icestick/counter/src/debug.sv deleted file mode 100644 index 9ba5ad0..0000000 --- a/examples/icestick/counter/src/debug.sv +++ /dev/null @@ -1,600 +0,0 @@ -`default_nettype none -`timescale 1ns / 1ps - -/* -This manta definition was autogenerated on 23 Feb 2023 at 18:10:18 by fischerm - -If this breaks or if you've got dank formal verification memes, -please contact fischerm [at] mit.edu. -*/ - -`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 = 12000000; - - 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 ( - .clk(clk), - .rst(rst), - - .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 */ - /* - - - ila 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 - - -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_ff @(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 - - -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 - - -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 - - -// 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 wire diff --git a/examples/icestick/counter/src/top_level.sv b/examples/icestick/counter/src/top_level.sv deleted file mode 100644 index b8923c6..0000000 --- a/examples/icestick/counter/src/top_level.sv +++ /dev/null @@ -1,33 +0,0 @@ -`default_nettype none -`timescale 1ns / 1ps - -`include "src/debug.sv" - -module top_level ( - input wire clk, - - input wire rs232_rx_ttl, - output logic rs232_tx_ttl - ); - - wire rst = 0; - - // Signal Generator - logic [7:0] count; - always_ff @(posedge clk) count <= count + 1; - - // debugger - manta manta( - .clk(clk), - .rst(rst), - .larry(count[0]), - .curly(count[1]), - .moe(count[2]), - .shemp(count[3:0]), - - .rxd(rs232_rx_ttl), - .txd(rs232_tx_ttl)); - -endmodule - -`default_nettype wire \ No newline at end of file diff --git a/examples/icestick/io_core/build.sh b/examples/icestick/io_core/build.sh new file mode 100755 index 0000000..b387a6e --- /dev/null +++ b/examples/icestick/io_core/build.sh @@ -0,0 +1,6 @@ +#!/bin/bash +yosys -p 'synth_ice40 -top top_level -json top_level.json' src/top_level.sv +nextpnr-ice40 --hx1k --json top_level.json --pcf pcf/top_level.pcf --asc top_level.asc +icepack top_level.asc top_level.bin +rm -f *.json +rm -f *.asc \ No newline at end of file diff --git a/examples/icestick/io_core/manta.yaml b/examples/icestick/io_core/manta.yaml new file mode 100644 index 0000000..80757ae --- /dev/null +++ b/examples/icestick/io_core/manta.yaml @@ -0,0 +1,16 @@ +--- +cores: + my_io_core: + type: io + + outputs: + LED0: 1 + LED1: 1 + LED2: 1 + LED3: 1 + LED4: 1 + +uart: + port: "auto" + baudrate: 115200 + clock_freq: 12000000 \ No newline at end of file diff --git a/examples/icestick/counter/pcf/top_level.pcf b/examples/icestick/io_core/pcf/top_level.pcf similarity index 100% rename from examples/icestick/counter/pcf/top_level.pcf rename to examples/icestick/io_core/pcf/top_level.pcf diff --git a/examples/icestick/io_core/run_io_core.py b/examples/icestick/io_core/run_io_core.py new file mode 100644 index 0000000..36cc4dd --- /dev/null +++ b/examples/icestick/io_core/run_io_core.py @@ -0,0 +1,45 @@ +from manta import Manta +from time import sleep + +m = Manta('manta.yaml') + +i = 0 +while True: + i = (i+1) % 5 + + if(i==0): + m.my_io_core.LED0.set(1) + m.my_io_core.LED1.set(0) + m.my_io_core.LED2.set(0) + m.my_io_core.LED3.set(0) + m.my_io_core.LED4.set(0) + + if(i==1): + m.my_io_core.LED0.set(0) + m.my_io_core.LED1.set(1) + m.my_io_core.LED2.set(0) + m.my_io_core.LED3.set(0) + m.my_io_core.LED4.set(0) + + if(i==2): + m.my_io_core.LED0.set(0) + m.my_io_core.LED1.set(0) + m.my_io_core.LED2.set(1) + m.my_io_core.LED3.set(0) + m.my_io_core.LED4.set(0) + + if(i==3): + m.my_io_core.LED0.set(0) + m.my_io_core.LED1.set(0) + m.my_io_core.LED2.set(0) + m.my_io_core.LED3.set(1) + m.my_io_core.LED4.set(0) + + if(i==4): + m.my_io_core.LED0.set(0) + m.my_io_core.LED1.set(0) + m.my_io_core.LED2.set(0) + m.my_io_core.LED3.set(0) + m.my_io_core.LED4.set(1) + + sleep(0.1) \ No newline at end of file diff --git a/examples/icestick/io_core/src/manta.v b/examples/icestick/io_core/src/manta.v new file mode 100644 index 0000000..ccb7a8f --- /dev/null +++ b/examples/icestick/io_core/src/manta.v @@ -0,0 +1,580 @@ +`default_nettype none +`timescale 1ns/1ps + +/* +This manta definition was generated on 02 Apr 2023 at 23:00:06 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. + +Here's an example instantiation of the Manta module you configured, +feel free to copy-paste this into your source! + +manta manta_inst ( + .clk(clk), + + .rx(rx), + .tx(tx), + + .LED0(LED0), + .LED1(LED1), + .LED2(LED2), + .LED3(LED3), + .LED4(LED4)); + +*/ + +module manta ( + input wire clk, + + input wire rx, + output reg tx, + + output reg LED0, + output reg LED1, + output reg LED2, + output reg LED3, + output reg LED4); + + rx_uart #(.CLOCKS_PER_BAUD(104)) 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; + +my_io_core my_io_core_inst( + .clk(clk), + + // ports + .LED0(LED0), + .LED1(LED1), + .LED2(LED2), + .LED3(LED3), + .LED4(LED4), + + // input port + .addr_i(brx_my_io_core_addr), + .wdata_i(brx_my_io_core_wdata), + .rdata_i(), + .rw_i(brx_my_io_core_rw), + .valid_i(brx_my_io_core_valid), + + // output port + .addr_o(), + .wdata_o(), + .rdata_o(my_io_core_btx_rdata), + .rw_o(my_io_core_btx_rw), + .valid_o(my_io_core_btx_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(104)) 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 my_io_core ( + input wire clk, + + // ports + output reg LED0, + output reg LED1, + output reg LED2, + output reg LED3, + output reg LED4, + + // input port + input wire [15:0] addr_i, + input wire [15:0] wdata_i, + input wire [15:0] rdata_i, + input wire rw_i, + input wire valid_i, + + // output port + output reg [15:0] addr_o, + output reg [15:0] wdata_o, + output reg [15:0] rdata_o, + output reg rw_o, + output reg valid_o + ); + +parameter BASE_ADDR = 0; +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 + 4)) begin + + if(!rw_i) begin // reads + case (addr_i) + 0: rdata_o <= {15'b0, LED0}; + 1: rdata_o <= {15'b0, LED1}; + 2: rdata_o <= {15'b0, LED2}; + 3: rdata_o <= {15'b0, LED3}; + 4: rdata_o <= {15'b0, LED4}; + endcase + end + + else begin // writes + case (addr_i) + 0: LED0 <= wdata_i[0]; + 1: LED1 <= wdata_i[0]; + 2: LED2 <= wdata_i[0]; + 3: LED3 <= wdata_i[0]; + 4: LED4 <= wdata_i[0]; + endcase + 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/icestick/io_core/src/top_level.sv b/examples/icestick/io_core/src/top_level.sv new file mode 100644 index 0000000..16551e5 --- /dev/null +++ b/examples/icestick/io_core/src/top_level.sv @@ -0,0 +1,32 @@ +`default_nettype none +`timescale 1ns / 1ps + +`include "src/manta.v" + +module top_level ( + input wire clk, + + output logic LED0, + output logic LED1, + output logic LED2, + output logic LED3, + output logic LED4, + + input wire rs232_rx_ttl, + output logic rs232_tx_ttl + ); + + manta manta_inst ( + .clk(clk), + + .rx(rs232_rx_ttl), + .tx(rs232_tx_ttl), + + .LED0(LED0), + .LED1(LED1), + .LED2(LED2), + .LED3(LED3), + .LED4(LED4)); +endmodule + +`default_nettype wire \ No newline at end of file diff --git a/examples/icestick/io_core/top_level.bin b/examples/icestick/io_core/top_level.bin new file mode 100644 index 0000000000000000000000000000000000000000..38882ebc01ce223deec33be3b17c19ba2003cef6 GIT binary patch literal 32220 zcmeHP4{#jSdH>$->U27trIVxBvJm#lRve{uol8(0x0T^pFbp;xqO`Pe+_YRshB$6W zRmW`&T%9LH6hh)yq_jXMGdYzqNfQzjrXlGx3@gKQD3DZ>2~!AQ&TUgDX;P)r7KdWo z-}m0`-QH<$bv9zgSo_&}@B8<@{oeO|`|j<#l}-_zI`ox0551Q}%je0xT3o)LrY4BG zNC;Zi;F#IxOeeVu<8%h$c^ym|?~=B;2*~Et60IZxNd!(K0@i8hF1f0^OdQq^pz%;co*{2KK)Oqi+C>~KDP+z*1Q z<>c%6L|oT=@Kl_cu&jA6C&I(x@a9d%S*ew|O4EzSvUXT3b#9rgtxHpjQqMoWF7-@_fmAQ$`(mhYZYs=Za!uQ&Di?w%<1Q%v-g|Rddwf>Z0880B*w-Y7d)Sm6{!qQTD(avVS^`Zj?5#N$dU} zO*c@a)Y_L1K-SxveF5FI$43f{G_9+I7QJ1Y-1L>v28vdyc*`Y7kLqPFDYdIbHqdnX zPhF`+f2U1;k$%=dky8IUqgeJ%ZL*OT^WEPu(l4>e7_hFn$x}3#CU}{pwH7_!NI5I6 z#R`O2el<^?UG6)4pK7G4`E8$DnVrYHsX+0HI10ThpEI^x(=BZKJY_4QUt^QqPNoxh zG>BIWU)SY^arIvmAAyejY?21Cv$dlwYOcW4?`2C4&7qA^b<@U6x5bN}xWIAew_wtK zsPV2q6h1kpaD(^k(O!zj0`*o4$9=swa9$1d<3W^9TeV zN6S16ujBNSRrxgpxAFGqH5hGnsN1aK^40StQ!c4dR+q8<1i>j-5;Z@Ie!B--TWM^BI<>bTWIl?_rS6eD4k z^M4OWqTOO!AIrCw|5rPqbzR-EnPB3i^v>TZF{=@z3R<$8H;-5!#5DTgfx*?Hz@%k; zLy~ICty>K*H?F^Ym}J`5FnU3|wq-}RB>Kkl_iuuGh2LDcJ=$cMXfuuSljQe>#37mW zLs*DkcQSh##m`Y6qqfFvJ#Y(R%Z#7oR(G#C!`!3nu z6uHXfL?b<8%uEYzb;P*i;E}@(sT}E8N=ix1sLi-$xw^%r+n@f4{5Y)&cJ789YW=#* zQ%}oJ6(02ejmG&ZktX%}q$qvts`7fG{(TRH>_nn#iYLKO{r%bljZErcis)5|i93U@ z>Pu*5gQo2sxF-tR<}e61hX&VlVO$Yqetr~Q(y8%~G!kuT{Q_Th+k>M-_e7d>iRKZ@ zob?_TnxGJ%0=MxD8oH*3*? zMm%-5L|sm8%95lHz2)#IZn_0Z%vkSfcCT(NF^_8UXQMem(}6P@z*at9CMt`Izg|W^ z5jk;=3s$1e_P;%f^laYwG&yy3$qHPT_>cIAn_9mhrIjL`aV@Jfs)8|+M$L1dBgI`K zKwakc4r9H-%5oXW3Ojd|k z5~hb^^|sPn=lM;`!Sp#aRS~`Yu9k-cj73I7reRVpq7H?6F2B9Xq# zXq9iD41KUv+1Sc+;PyzLRVEttd_2@e_$1^o%@2fh8lt%H5x#hrwcJnF{S>$ysxTgW zY&KKDJT?SjGhzHSm<(A@&}Jgw$GsWVvrp#?YHsN!;Vzx2#L~GHfut_qX!&e9V`=$@ zm|C=#P0uoFj?pHgG@IrnCTl{a4CQAj{K;ImI|@W5et0IVfRFoo-bGH9Dx-h|Cd0gF zTt%}Nas!i6i{iYTSU5PE)4a*(@ljmYFs1I|yfk{J(Viz6F2i``0a8Vz$6qhc z_PVS|)tTQtVKZSoI76&Gk~3jlqz#)1;U1QM!)IM($H8PlFd zit;gL!lGQ-ygD4_HNgL-0l$`QVK#xu@QPKdmB3_21CyQkD|-CXS6xK5m!Dd2UCqeO zqRZki&iA+2C+>{YY|rluLNdb&_R#{VNKI{$CAqYa=!z@uW@;gblqbeL{UMa+k0sl4 z7bEE&44%|0&Zv&{4Fp5mMT6CsO&hrD^8 zJu{?D1}}iuD`!{v5%TM@xbAB>=WGayB_@_$Ba?bC=X}(>XJ71?R+?971lfNA!ohd$ zC1$$jyZujjOoRc2AVDLJD-7G^MYolT9e3Rl*2A}z_!12OlYZ=SwgipzJjMJPkeU)J zU^b7D_5n-TR7{oE)M&vZzE{<&{3zQ;VHQwq{j&K|TvN;u z^H<%54iooHC?^p}B9KI2$s=$E zYvI!Q;4Yo1v(%D0OZ__Q-Ad~6tWOaf)LT-s;jZE(_FU5A*!v1b8$zaaJ~csf!Rkjs zDho{Jg6Ckle$&hPNhPEmMunX~QxDcr*Qs_ue`^V^F4PG8!e`50-FxQ+xr3~gb8hOW>7Qici7h9K8w!a;x) z`@*M3p_+F6;ld(S^6}d49slv|3^)qfgLqTgy66e$MVKr=Da|I)^UF|f`^S5zqzT=O z1}F!s^$s+OIe1uNFs(+pNt-KJc7X%qZ;$m`=RG7mDEJ_}0mfR%9hy~m!&5OOF+_v* zao|gJe)hiR~dCOz@>Uzgu80a<{2UO7H?5<0lSu4-kYHB>0-U z_cLxeHR)Gib&lV6wbT!A5UD(+R~H8O+3w9V6_eXec}maPK@nkyE_EQ*l?70r_aEnh z+9yw%XJ4xTv2iho5Ms6ZNX7(@W%Oy_r%v5AfVwtGefOU&@{6ZbLa zFaJBYM*1QL6CWhe6EexJjU4JmoUKhSR_~rMxT^N`O!))-POUq{M*`D-Jgta&@WWaN z)$dIOH=)q4YvZ?04)EP$_dYr|6BH5jtkehAcsvS_Hh&}{-NSyX=}Qnh$|^L7*IT*E z1n>B^b}Fj2MB+H)6D|CUEb6bw zX&K^do1^&i6U>r$XDMVi`7 zmf^GWo_Tg9lk(sQ%pjKV60iz)PnxpdE@>|i4X$|%6WsgI|CWG?d1Zr}SDS}qI!Wqa$HCz)oj#%Fub4W2(xfCaj@1cHxF z#26#G78!v_+W1XIChwOn629)ER0fr&5gT5n@#iTHg4B@Pe%(_FCc6$6B+mMtQ0M!< zOf~3mY`+I3J$1**{8{NUw2LmlCjcAe1#ME{FLLzCF>y427| zmP;*vD4DoaDDG%*lEjCf1tQ7mLtHNSqCAn|XwZO7WAG*Q%qZ(2b}qg9$FPqQzCoh< z<=JV)y#bf94+psJvxw=49%|VqYb18Qz$vLf4w~+uCOeK-xD`Y)HcHme5gnV`Cqd?9 zr&(NB=7YFQBWdb7&gLEQ&s>*^+Q#8Z01o}rOn`ImALa7ZWg8r+Lgur3s{}A3EC5r~ z!x!K@Fx3=-95R`2jj1EW*sNSB1vs(w1mFgbAG!HR+WDqRQz_wfaD z(o81ri+8TtSy8lw%q1SIRVcIvzuW;?;qu`4n091_dw0kr0Kj+aec zrhEDe$hb#4-aZ0_3BGnZ&_nNZnn^gC$2aRs$F4-%weL_^OJ6wA#(jc`B9@eJhx>S%f`_;QDyh@u0fQl48}G*qUTrK ztZ=pLP#|<@Hb8^kbzqUtso|z0rO!rSm5&CUONDEI7uUVOhsr@LiB``A8bP0a@H_0K z7tnq`a_#$79jpC2l;U&hX(P~ER;YBSaANhz`>&3G&xgva>Eh}VJ;~n{Q`}dAReRS+e%`i>ga(dS&@|fbfMCY~GlejcY-pJh0)Nv~- z%?X<8Z=ZC5The@ltfW>osKzeq_wH0J9t1uJul>_^>CbU`ftOs&M#DeYo{v@Rx`$ta z*Vt*unyPVzWuRH|#Jy4VT;uBMCjlj@=gjJBDPKoWr@0?Rc56L^+PmVxD({bb%r z1at(tM6w@9N+F5BvX4NrA6WJ!kOUx!K$n&52a+mCBCzZukn9JReF-E1NFvZ>C;Ne< b3X%vc`v@fafn{F;NdS@vtbfxFM2h|&eBt|q literal 0 HcmV?d00001