Added a test for IDELAY with single bit data transmission and error counting.

Signed-off-by: Maciej Kurc <mkurc@antmicro.com>
This commit is contained in:
Maciej Kurc 2019-08-30 16:51:10 +02:00
parent ce926fc359
commit 0e4c5be5d5
12 changed files with 1087 additions and 2 deletions

View File

@ -30,6 +30,33 @@ Consider the `JXADC` connector on the Basys3 board as seen when looking at the b
**The oscilloscope must have bandwidth of at least 100MHz.**
## 1. basys3_idelay_const
## 2. basys3_idelay_const
This design generates 32 independently shifted 50MHz square waves using constant delay IDELAY blocks. Delays between individual signals can be measured using an oscilloscope. Due to the fact that each delay step is about 100-150ps and the FPGA fabric + IOBs also introduce their own delays, actual delay values may be hard to measure.
This design generates 32 independently shifted 50MHz square waves using constant delay IDELAY blocks. Delays between individual signals can be measured using an oscilloscope. Due to the fact that each delay step is about 100-150ps and the FPGA fabric + IOBs also introduce their own delays, actual delay values may be hard to measure.
## 3. basys3_idelay_histogram
This design transmitts a pseudo-random data stream throught one output pin and then receives it through another one with the use of IDELAY. A physical loopback is required. The received data is compared with the transmitted one. Receive errors are counted separately for each one of 32 possible delay settings of the IDELAY bel. Values of 32 error counters are periodically pinted using the UART as an ASCII string.
There is a control state machine which performs the following sequence once per ~0.5s (adjustable).
1. Set delay of the IDELAY bel.
2. Wait for it to stabilize (a few clock cycles)
3. Compare received and transmitted data and count errors. Do it for some period of time (adjustable).
4. Repeat steps 1-4 for all 32 delay steps
5. Output error counters through the UART
6. Wait
The physical loopback has to be connected between `JXADC.7` and `JXADC.8` pins.
Example UART output:
```
...
0027F_000277_00026D_00025C_00027C_00028B_000265_000275_000265_000271_000275_000255_00027A_000280_00027B_000265_00027B_00027A_00025D_000263_000256_00026F_000293_000268_000286_000260_000269_000275_000266_00026D_000273_000272
00281_000271_000273_00026B_000273_000271_00025F_000279_00027D_000283_000266_000279_000274_00025D_000261_000260_00026F_000287_00026E_000289_000261_000267_00027A_00026C_00026D_000270_00026C_00027C_000251_000266_00027A_000283
00271_000255_00027D_000283_000283_00025B_00027E_000271_000263_000259_000262_000270_00027E_00026F_00027D_000267_00026C_00026E_00026E_00027B_00026F_00026D_000279_000250_00026E_00027E_000282_000267_000270_000262_000237_000284
...
```
There are 32 hex numbers separated by "_". Each one correspond to one error counter.

View File

@ -0,0 +1,205 @@
`include "src/idelay_histogram.v"
`include "src/idelay_calibrator.v"
`include "src/error_counter.v"
`include "src/message_formatter.v"
`include "src/lfsr.v"
`include "src/simpleuart.v"
`default_nettype none
// ============================================================================
module top
(
input wire clk,
input wire rx,
output wire tx,
input wire [15:0] sw,
output wire [15:0] led,
output wire ja1,
output wire ja2,
output wire ja3,
output wire ja4,
output wire ja7,
output wire ja8,
output wire ja9,
output wire ja10,
output wire jb1,
output wire jb2,
output wire jb3,
output wire jb4,
output wire jb7,
output wire jb8,
output wire jb9,
output wire jb10,
output wire jc1,
output wire jc2,
output wire jc3,
output wire jc4,
output wire jc7,
output wire jc8,
output wire jc9,
output wire jc10,
output wire xadc1_p,
output wire xadc2_p,
output wire xadc3_p,
output wire xadc4_p,
input wire xadc1_n,
output wire xadc2_n,
output wire xadc3_n,
output wire xadc4_n
);
// ============================================================================
// Clock & reset
reg [3:0] rst_sr;
initial rst_sr <= 4'hF;
always @(posedge clk)
if (sw[0])
rst_sr <= 4'hF;
else
rst_sr <= rst_sr >> 1;
wire pll_clkfb;
wire pll_locked;
wire CLK100;
wire CLK200;
wire CLK400;
PLLE2_BASE #
(
.CLKFBOUT_MULT (8),
.CLKOUT0_DIVIDE (8),
.CLKOUT1_DIVIDE (4),
.CLKOUT2_DIVIDE (2)
)
pll
(
.CLKIN1 (clk),
.CLKFBIN (pll_clkfb),
.CLKFBOUT (pll_clkfb),
.CLKOUT0 (CLK100),
.CLKOUT1 (CLK200),
.CLKOUT2 (CLK400),
.RST (rst_sr[0]),
.LOCKED (pll_locked)
);
// ============================================================================
// IDELAY calibrator
wire cal_rdy;
idelay_calibrator cal
(
.refclk (CLK400),
.rst (rst_sr[0] || !pll_locked),
.rdy (cal_rdy)
);
wire RST = rst_sr[0] || !cal_rdy;
// ============================================================================
wire sig_out;
wire sig_inp;
wire [4:0] delay;
wire sig_ref_i;
wire sig_ref_o;
idelay_histogram #
(
.UART_PRESCALER (868)
)
idelay_histogram
(
.CLK (CLK100),
.RST (RST),
.UART_RX (rx),
.UART_TX (tx),
.TEST (sw[1]),
.O (sig_out),
.I (sig_inp),
.REF_O (sig_ref_o),
.REF_I (sig_ref_i),
.DELAY (delay)
);
// ============================================================================
// I/O connections
reg [23:0] heartbeat_cnt;
always @(posedge CLK100)
heartbeat_cnt <= heartbeat_cnt + 1;
assign led[ 0] = heartbeat_cnt[23];
assign led[ 1] = cal_rdy;
assign led[ 2] = 1'b0;
assign led[ 3] = 1'b0;
assign led[ 4] = 1'b0;
assign led[ 5] = 1'b0;
assign led[ 6] = 1'b0;
assign led[ 7] = 1'b0;
assign led[ 8] = 1'b0;
assign led[ 9] = 1'b0;
assign led[10] = 1'b0;
assign led[11] = delay[0];
assign led[12] = delay[1];
assign led[13] = delay[2];
assign led[14] = delay[3];
assign led[15] = delay[4];
assign ja1 = 1'b0;
assign ja2 = 1'b0;
assign ja3 = 1'b0;
assign ja4 = 1'b0;
assign ja7 = 1'b0;
assign ja8 = 1'b0;
assign ja9 = 1'b0;
assign ja10 = 1'b0;
assign jb1 = 1'b0;
assign jb2 = 1'b0;
assign jb3 = 1'b0;
assign jb4 = 1'b0;
assign jb7 = 1'b0;
assign jb8 = 1'b0;
assign jb9 = 1'b0;
assign jb10 = 1'b0;
assign jc1 = 1'b0;
assign jc2 = 1'b0;
assign jc3 = 1'b0;
assign jc4 = 1'b0;
assign jc7 = 1'b0;
assign jc8 = 1'b0;
assign jc9 = 1'b0;
assign jc10 = 1'b0;
assign xadc1_p = sig_ref_i;
assign xadc2_p = sig_ref_o;
assign xadc3_p = 1'b0;
assign xadc4_p = 1'b0;
//assign xadc1_n = 1'b0;
assign xadc2_n = sig_out;
assign xadc3_n = 1'b0;
assign xadc4_n = 1'b0;
assign sig_inp = xadc1_n;
endmodule

View File

@ -0,0 +1,66 @@
`default_nettype none
`timescale 1ns / 1ps
`include "../src/error_counter.v"
// ============================================================================
module tb;
// ============================================================================
reg CLK;
initial CLK <= 1'b0;
always #0.5 CLK <= !CLK;
reg [3:0] rst_sr;
initial rst_sr <= 4'hF;
always @(posedge CLK) rst_sr <= rst_sr >> 1;
wire RST;
assign RST = rst_sr[0];
// ============================================================================
initial begin
$dumpfile("waveforms.vcd");
$dumpvars;
end
integer cycle_cnt;
initial cycle_cnt <= 0;
always @(posedge CLK)
if (!RST) cycle_cnt <= cycle_cnt + 1;
always @(posedge CLK)
if (!RST && cycle_cnt >= 150)
$finish;
// ============================================================================
//re i_stb = (cycle_cnt == 10);
//re [32*2-1:0] i_dat = 64'h01234567_ABCD4321;
wire o_stb;
wire [4*8-1:0] o_dat;
error_counter #
(
.COUNT_WIDTH (8),
.DELAY_TAPS (4)
)
dut
(
.CLK (CLK),
.RST (RST),
.O_STB (o_stb),
.O_DAT (o_dat)
);
always @(posedge CLK)
if (o_stb)
$display("%X", o_dat);
endmodule

View File

@ -0,0 +1,70 @@
`default_nettype none
`timescale 1ns / 1ps
`include "../src/message_formatter.v"
// ============================================================================
module tb;
// ============================================================================
reg CLK;
initial CLK <= 1'b0;
always #0.5 CLK <= !CLK;
reg [3:0] rst_sr;
initial rst_sr <= 4'hF;
always @(posedge CLK) rst_sr <= rst_sr >> 1;
wire RST;
assign RST = rst_sr[0];
// ============================================================================
initial begin
$dumpfile("waveforms.vcd");
$dumpvars;
end
integer cycle_cnt;
initial cycle_cnt <= 0;
always @(posedge CLK)
if (!RST) cycle_cnt <= cycle_cnt + 1;
always @(posedge CLK)
if (!RST && cycle_cnt >= 150)
$finish;
// ============================================================================
wire i_stb = (cycle_cnt == 10);
wire [32*2-1:0] i_dat = 64'h01234567_ABCD4321;
wire o_stb;
wire [7:0] o_dat;
message_formatter #
(
.WIDTH (32),
.COUNT (2),
.TX_INTERVAL (4)
)
dut
(
.CLK (CLK),
.RST (RST),
.I_STB (i_stb),
.I_DAT (i_dat),
.O_STB (o_stb),
.O_DAT (o_dat)
);
always @(posedge CLK)
if (o_stb)
$display("%c", o_dat);
endmodule

View File

@ -0,0 +1,22 @@
#!/bin/bash
set -e
# Check args
if [ "$#" -ne 1 ]; then
echo "Usage: run_vivado.sh <testbench file>"
exit -1
fi
# Check if testbench exists
if [ ! -f $1 ]; then
echo "Testbench $1 not found!"
exit -1
fi
# Compile
iverilog -v -g2005 -s tb -o testbench.vvp $1
# Run
vvp -v testbench.vvp

View File

@ -0,0 +1,18 @@
#!/bin/bash
set -e
# Check args
if [ "$#" -ne 1 ]; then
echo "Usage: run_vivado.sh <testbench file>"
exit -1
fi
# Check if testbench exists
if [ ! -f $1 ]; then
echo "Testbench $1 not found!"
exit -1
fi
# Run Vivado
TESTBENCH_TITLE=$(basename $1 .v) ${XRAY_VIVADO} -mode batch -source sim.tcl -nojournal -verbose -log vivado.log

View File

@ -0,0 +1,20 @@
create_project -force -part xc7a35ticsg324-1L $::env(TESTBENCH_TITLE) $::env(TESTBENCH_TITLE)
read_verilog $::env(TESTBENCH_TITLE).v
set_property top tb [get_filesets sim_1]
synth_design -top tb -verbose
set_property xsim.simulate.log_all_signals true [get_filesets sim_1]
set_property xsim.simulate.runtime 0 [get_filesets sim_1]
launch_simulation -verbose
restart
open_vcd ../../../../waveforms.vcd
run -all
flush_vcd
close_vcd

View File

@ -0,0 +1,113 @@
`default_nettype none
module error_counter #
(
parameter COUNT_WIDTH = 24,
parameter DELAY_TAPS = 32,
parameter TRIGGER_INTERVAL = 20,//100000000,
parameter HOLDOFF_TIME = 4,//10,
parameter MEASURE_TIME = 10//50000
)
(
input wire CLK,
input wire RST,
input wire I_STB,
input wire I_ERR,
output wire DLY_LD,
output wire [$clog2(DELAY_TAPS)-1:0] DLY_CNT,
output wire O_STB,
output wire [COUNT_WIDTH*DELAY_TAPS-1:0] O_DAT
);
// ============================================================================
// FSM
integer fsm;
localparam FSM_IDLE = 'h00;
localparam FSM_SETUP = 'h10;
localparam FSM_HOLDOFF = 'h20;
localparam FSM_PREPARE = 'h30;
localparam FSM_MEASURE = 'h40;
localparam FSM_STORE = 'h50;
localparam FSM_OUTPUT = 'h60;
// ============================================================================
// Counters
reg [32:0] ps_cnt;
reg [$clog2(DELAY_TAPS)-1:0] dly_cnt;
initial ps_cnt <= TRIGGER_INTERVAL - 1;
always @(posedge CLK)
case (fsm)
FSM_IDLE: ps_cnt <= ps_cnt - 1;
FSM_SETUP: ps_cnt <= HOLDOFF_TIME - 1;
FSM_HOLDOFF: ps_cnt <= ps_cnt - 1;
FSM_PREPARE: ps_cnt <= MEASURE_TIME - 1;
FSM_MEASURE: ps_cnt <= ps_cnt - 1;
FSM_OUTPUT: ps_cnt <= TRIGGER_INTERVAL - 1;
endcase
always @(posedge CLK)
case (fsm)
FSM_IDLE: dly_cnt <= 0;
FSM_STORE: dly_cnt <= dly_cnt + 1;
endcase
// ============================================================================
// IDELAY control
assign DLY_LD = (fsm == FSM_SETUP);
assign DLY_CNT = dly_cnt;
// ============================================================================
// Error counter and output shift register
reg [(COUNT_WIDTH*DELAY_TAPS)-1:0] o_dat_sr;
reg [COUNT_WIDTH-1:0] err_cnt;
always @(posedge CLK)
case (fsm)
FSM_PREPARE: err_cnt <= 0;
FSM_MEASURE: if(I_STB) err_cnt <= err_cnt + I_ERR;
endcase
always @(posedge CLK)
if (fsm == FSM_STORE)
o_dat_sr <= (o_dat_sr << COUNT_WIDTH) | err_cnt;
// ============================================================================
// Control FSM
always @(posedge CLK)
if (RST)
fsm <= FSM_IDLE;
else case (fsm)
FSM_IDLE: if (ps_cnt == 0) fsm <= FSM_SETUP;
FSM_SETUP: fsm <= FSM_HOLDOFF;
FSM_HOLDOFF: if (ps_cnt == 0) fsm <= FSM_PREPARE;
FSM_PREPARE: fsm <= FSM_MEASURE;
FSM_MEASURE: if (ps_cnt == 0) fsm <= FSM_STORE;
FSM_STORE: if (dly_cnt == (DELAY_TAPS-1))
fsm <= FSM_OUTPUT;
else
fsm <= FSM_SETUP;
FSM_OUTPUT: fsm <= FSM_IDLE;
endcase
// ============================================================================
// Output
assign O_STB = (fsm == FSM_OUTPUT);
assign O_DAT = o_dat_sr;
endmodule

View File

@ -0,0 +1,216 @@
module idelay_histogram #
(
parameter UART_PRESCALER = 868 // UART prescaler
)
(
// Closk & reset
input wire CLK,
input wire RST,
// UART
input wire UART_RX,
output wire UART_TX,
// TEST - internal loopback
input wire TEST,
// Input and output pins
output wire O,
input wire I,
output wire REF_O,
output wire REF_I,
// IDELAY delay setting output
output wire [4:0] DELAY
);
// ============================================================================
// Data generator
reg [1:0] ce_cnt;
wire ce;
always @(posedge CLK)
ce_cnt <= ce_cnt + 1;
assign ce = (ce_cnt == 0);
// LFSR
wire [15:0] lfsr_r;
lfsr lfsr
(
.clk (CLK),
.rst (RST),
.ce (ce),
.r (lfsr_r)
);
reg o_stb;
wire o_dat;
always @(posedge CLK)
if (RST)
o_stb <= 1'b0;
else
o_stb <= ce;
assign o_dat = lfsr_r[0];
assign O = o_dat;
// ============================================================================
// Data input with IDELAY
wire dly_dat;
wire dly_ld;
wire [5:0] dly_cnt;
IDELAYE2 #
(
.IDELAY_TYPE ("VAR_LOAD"),
.DELAY_SRC ("IDATAIN")
)
idelay
(
.IDATAIN (I),
.DATAOUT (dly_dat),
.C (CLK),
.LD (dly_ld),
.CNTVALUEIN (dly_cnt),
.CNTVALUEOUT(DELAY)
);
assign OI = dly_dat;
// ============================================================================
// Data comparator
reg o_dat_r;
reg cmp_s0_stb;
reg cmp_s0_o_dat;
reg cmp_s0_i_dat;
reg cmp_s1_stb;
reg cmp_s1_err;
always @(posedge CLK)
o_dat_r <= o_dat;
always @(posedge CLK)
if (RST)
cmp_s0_stb <= 1'b0;
else
cmp_s0_stb <= o_stb;
always @(posedge CLK)
if (o_stb)
cmp_s0_o_dat <= o_dat_r;
always @(posedge CLK)
if (o_stb)
cmp_s0_i_dat <= (TEST) ? O : dly_dat;
always @(posedge CLK)
if (RST)
cmp_s1_stb <= 1'b0;
else
cmp_s1_stb <= cmp_s0_stb;
always @(posedge CLK)
cmp_s1_err <= cmp_s0_o_dat ^ cmp_s0_i_dat;
assign REF_O = o_dat_r;
assign REF_I = dly_dat;
// ============================================================================
// Error counter
wire cnt_stb;
wire [32*24-1:0] cnt_dat;
error_counter #
(
.COUNT_WIDTH (24),
.DELAY_TAPS (32),
.TRIGGER_INTERVAL (50000000),
.HOLDOFF_TIME (100),
.MEASURE_TIME (10000)
)
error_counter
(
.CLK (CLK),
.RST (RST),
.I_STB (cmp_s1_stb),
.I_ERR (cmp_s1_err),
.DLY_LD (dly_ld),
.DLY_CNT(dly_cnt),
.O_STB (cnt_stb),
.O_DAT (cnt_dat)
);
// ============================================================================
// Message formatter
wire uart_x_stb;
wire [7:0] uart_x_dat;
message_formatter #
(
.WIDTH (24),
.COUNT (32),
.TX_INTERVAL (UART_PRESCALER * 11) // 10 bits plus one more.
)
message_formatter
(
.CLK (CLK),
.RST (RST),
.I_STB (cnt_stb),
.I_DAT (cnt_dat),
.O_STB (uart_x_stb),
.O_DAT (uart_x_dat)
);
// ============================================================================
// UART
// Baudrate prescaler initializer
reg [7:0] reg_div_we_sr;
wire reg_div_we;
always @(posedge CLK)
if (RST) reg_div_we_sr <= 8'h01;
else reg_div_we_sr <= {reg_div_we_sr[6:0], 1'd0};
assign reg_div_we = reg_div_we_sr[7];
// The UART
simpleuart uart
(
.clk (CLK),
.resetn (!RST),
.ser_rx (UART_RX),
.ser_tx (UART_TX),
.reg_div_we ({reg_div_we, reg_div_we, reg_div_we, reg_div_we}),
.reg_div_di (UART_PRESCALER),
.reg_div_do (),
.reg_dat_we (uart_x_stb),
.reg_dat_re (1'd0),
.reg_dat_di ({24'd0, uart_x_dat}),
.reg_dat_do (),
.reg_dat_wait ()
);
// Debug
always @(posedge CLK)
if (uart_x_stb)
$display("%c", uart_x_dat);
endmodule

View File

@ -0,0 +1,28 @@
`default_nettype none
// ============================================================================
module lfsr #
(
parameter WIDTH = 16,
parameter [WIDTH-1:0] POLY = 16'hD008,
parameter [WIDTH-1:0] SEED = 1
)
(
input wire clk,
input wire rst,
input wire ce,
output reg [WIDTH-1:0] r
);
wire feedback = ^(r & POLY);
always @(posedge clk) begin
if(rst) begin
r <= SEED;
end else if(ce) begin
r <= {r[WIDTH-2:0], feedback};
end
end
endmodule

View File

@ -0,0 +1,161 @@
`default_nettype none
// ============================================================================
module message_formatter #
(
parameter WIDTH = 24, // Word length in bits. MUST be a multiply of 4
parameter COUNT = 2, // Word count
parameter TX_INTERVAL = 4 // Character transmission interval
)
(
// Clock and reset
input wire CLK,
input wire RST,
// Data input
input wire I_STB,
input wire [(WIDTH*COUNT)-1:0] I_DAT,
// ASCII output
output wire O_STB,
output wire [7:0] O_DAT
);
// ============================================================================
// Total input data word width
localparam TOTAL_WIDTH = WIDTH * COUNT;
// ============================================================================
// FSM states
integer fsm;
localparam FSM_IDLE = 'h00;
localparam FSM_TX_HEX = 'h11;
localparam FSM_TX_CR = 'h21;
localparam FSM_TX_LF = 'h22;
localparam FSM_TX_SEP = 'h31;
// ============================================================================
// TX interval counter
reg [24:0] tx_dly_cnt;
reg tx_req;
wire tx_rdy;
always @(posedge CLK)
if (RST)
tx_dly_cnt <= -1;
else if (!tx_rdy)
tx_dly_cnt <= tx_dly_cnt - 1;
else if ( tx_rdy && tx_req)
tx_dly_cnt <= TX_INTERVAL - 2;
assign tx_rdy = tx_dly_cnt[24];
always @(posedge CLK)
if (RST)
tx_req <= 1'b0;
else case (fsm)
FSM_TX_HEX: tx_req <= 1'b1;
FSM_TX_SEP: tx_req <= 1'b1;
FSM_TX_CR: tx_req <= 1'b1;
FSM_TX_LF: tx_req <= 1'b1;
default: tx_req <= 1'b0;
endcase
// ============================================================================
// Word and char counter
reg [7:0] char_cnt;
reg [7:0] word_cnt;
always @(posedge CLK)
if (fsm == FSM_IDLE || fsm == FSM_TX_SEP)
char_cnt <= (WIDTH/4) - 1;
else if (tx_rdy && fsm == FSM_TX_HEX)
char_cnt <= char_cnt - 1;
always @(posedge CLK)
if (fsm == FSM_IDLE)
word_cnt <= COUNT - 1;
else if (tx_rdy && fsm == FSM_TX_SEP)
word_cnt <= word_cnt - 1;
// ============================================================================
// Data shift register
reg [TOTAL_WIDTH-1:0] sr_reg;
wire [3:0] sr_dat;
always @(posedge CLK)
if (fsm == FSM_IDLE && I_STB)
sr_reg <= I_DAT;
else if (fsm == FSM_TX_HEX && tx_rdy)
sr_reg <= sr_reg << 4;
assign sr_dat = sr_reg[TOTAL_WIDTH-1:TOTAL_WIDTH-4];
// ============================================================================
// Control FSM
always @(posedge CLK)
if (RST)
fsm <= FSM_IDLE;
else case (fsm)
FSM_IDLE: if (I_STB) fsm <= FSM_TX_HEX;
FSM_TX_HEX:
if (tx_rdy && (char_cnt == 0) && (word_cnt == 0))
fsm <= FSM_TX_CR;
else if (tx_rdy && (char_cnt == 0)) fsm <= FSM_TX_SEP;
else if (tx_rdy && (char_cnt != 0)) fsm <= FSM_TX_HEX;
FSM_TX_SEP: if (tx_rdy) fsm <= FSM_TX_HEX;
FSM_TX_CR: if (tx_rdy) fsm <= FSM_TX_LF;
FSM_TX_LF: if (tx_rdy) fsm <= FSM_IDLE;
endcase
// ============================================================================
// Data to ASCII converter
reg o_stb;
reg [7:0] o_dat;
always @(posedge CLK or posedge RST)
if (RST)
o_stb <= 1'd0;
else
o_stb <= tx_req & tx_rdy;
always @(posedge CLK)
if (fsm == FSM_TX_CR)
o_dat <= 8'h0D;
else if (fsm == FSM_TX_LF)
o_dat <= 8'h0A;
else if (fsm == FSM_TX_SEP)
o_dat <= "_";
else if (fsm == FSM_TX_HEX) case (sr_dat)
4'h0: o_dat <= "0";
4'h1: o_dat <= "1";
4'h2: o_dat <= "2";
4'h3: o_dat <= "3";
4'h4: o_dat <= "4";
4'h5: o_dat <= "5";
4'h6: o_dat <= "6";
4'h7: o_dat <= "7";
4'h8: o_dat <= "8";
4'h9: o_dat <= "9";
4'hA: o_dat <= "A";
4'hB: o_dat <= "B";
4'hC: o_dat <= "C";
4'hD: o_dat <= "D";
4'hE: o_dat <= "E";
4'hF: o_dat <= "F";
endcase
assign O_STB = o_stb;
assign O_DAT = o_dat;
endmodule

View File

@ -0,0 +1,139 @@
/*
* PicoSoC - A simple example SoC using PicoRV32
*
* Copyright (C) 2017 Clifford Wolf <clifford@clifford.at>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
`default_nettype wire
module simpleuart (
input clk,
input resetn,
output ser_tx,
input ser_rx,
input [3:0] reg_div_we,
input [31:0] reg_div_di,
output [31:0] reg_div_do,
input reg_dat_we,
input reg_dat_re,
input [31:0] reg_dat_di,
output [31:0] reg_dat_do,
output reg_dat_wait
);
reg [31:0] cfg_divider;
reg [3:0] recv_state;
reg [31:0] recv_divcnt;
reg [7:0] recv_pattern;
reg [7:0] recv_buf_data;
reg recv_buf_valid;
reg [9:0] send_pattern;
reg [3:0] send_bitcnt;
reg [31:0] send_divcnt;
reg send_dummy;
assign reg_div_do = cfg_divider;
assign reg_dat_wait = reg_dat_we && (send_bitcnt || send_dummy);
assign reg_dat_do = recv_buf_valid ? recv_buf_data : ~0;
always @(posedge clk) begin
if (!resetn) begin
cfg_divider <= 1;
end else begin
if (reg_div_we[0]) cfg_divider[ 7: 0] <= reg_div_di[ 7: 0];
if (reg_div_we[1]) cfg_divider[15: 8] <= reg_div_di[15: 8];
if (reg_div_we[2]) cfg_divider[23:16] <= reg_div_di[23:16];
if (reg_div_we[3]) cfg_divider[31:24] <= reg_div_di[31:24];
end
end
always @(posedge clk) begin
if (!resetn) begin
recv_state <= 0;
recv_divcnt <= 0;
recv_pattern <= 0;
recv_buf_data <= 0;
recv_buf_valid <= 0;
end else begin
recv_divcnt <= recv_divcnt + 1;
if (reg_dat_re)
recv_buf_valid <= 0;
case (recv_state)
0: begin
if (!ser_rx)
recv_state <= 1;
recv_divcnt <= 0;
end
1: begin
if (2*recv_divcnt > cfg_divider) begin
recv_state <= 2;
recv_divcnt <= 0;
end
end
10: begin
if (recv_divcnt > cfg_divider) begin
recv_buf_data <= recv_pattern;
recv_buf_valid <= 1;
recv_state <= 0;
end
end
default: begin
if (recv_divcnt > cfg_divider) begin
recv_pattern <= {ser_rx, recv_pattern[7:1]};
recv_state <= recv_state + 1;
recv_divcnt <= 0;
end
end
endcase
end
end
assign ser_tx = send_pattern[0];
always @(posedge clk) begin
if (reg_div_we)
send_dummy <= 1;
send_divcnt <= send_divcnt + 1;
if (!resetn) begin
send_pattern <= ~0;
send_bitcnt <= 0;
send_divcnt <= 0;
send_dummy <= 1;
end else begin
if (send_dummy && !send_bitcnt) begin
send_pattern <= ~0;
send_bitcnt <= 15;
send_divcnt <= 0;
send_dummy <= 0;
end else
if (reg_dat_we && !send_bitcnt) begin
send_pattern <= {1'b1, reg_dat_di[7:0], 1'b0};
send_bitcnt <= 10;
send_divcnt <= 0;
end else
if (send_divcnt > cfg_divider && send_bitcnt) begin
send_pattern <= {1'b1, send_pattern[9:1]};
send_bitcnt <= send_bitcnt - 1;
send_divcnt <= 0;
end
end
end
endmodule