add working icestick io core example

This commit is contained in:
Fischer Moseley 2023-04-02 23:16:25 -04:00
parent 20f28be214
commit 1b27a6f948
11 changed files with 679 additions and 658 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

Binary file not shown.