OpenCores
URL https://opencores.org/ocsvn/wbuart32/wbuart32/trunk

Subversion Repositories wbuart32

Compare Revisions

  • This comparison shows the changes necessary to convert path
    /wbuart32/trunk/rtl
    from Rev 2 to Rev 5
    Reverse comparison

Rev 2 → Rev 5

/Makefile
48,6 → 48,7
.PHONY: test
test: $(VDIRFB)/Vtxuart__ALL.a
test: $(VDIRFB)/Vrxuart__ALL.a
test: $(VDIRFB)/Vwbuart__ALL.a
 
$(VDIRFB)/Vrxuart__ALL.a: $(VDIRFB)/Vrxuart.h $(VDIRFB)/Vrxuart.cpp
$(VDIRFB)/Vrxuart__ALL.a: $(VDIRFB)/Vrxuart.mk
57,6 → 58,10
$(VDIRFB)/Vtxuart__ALL.a: $(VDIRFB)/Vtxuart.mk
$(VDIRFB)/Vtxuart.h $(VDIRFB)/Vtxuart.cpp $(VDIRFB)/Vtxuart.mk: txuart.v
 
$(VDIRFB)/Vwbuart__ALL.a: $(VDIRFB)/Vwbuart.h $(VDIRFB)/Vwbuart.cpp
$(VDIRFB)/Vwbuart__ALL.a: $(VDIRFB)/Vwbuart.mk
$(VDIRFB)/Vwbuart.h $(VDIRFB)/Vwbuart.cpp $(VDIRFB)/Vwbuart.mk: wbuart.v ufifo.v txuart.v rxuart.v
 
$(VDIRFB)/V%.cpp $(VDIRFB)/V%.h $(VDIRFB)/V%.mk: $(FBDIR)/%.v
verilator -cc $*.v
 
/rxuart.v
2,7 → 2,7
//
// Filename: rxuart.v
//
// Project: FPGA library
// Project: wbuart32, a full featured UART with simulator
//
// Purpose: Receive and decode inputs from a single UART line.
//
111,15 → 111,13
`define RXU_RESET_IDLE 4'he
`define RXU_IDLE 4'hf
 
module rxuart(i_clk, i_reset, i_setup, i_uart, o_wr, o_data, o_break,
module rxuart(i_clk, i_reset, i_setup, i_uart_rx, o_wr, o_data, o_break,
o_parity_err, o_frame_err, o_ck_uart);
// parameter // CLOCKS_PER_BAUD = 25'd004340,
// BREAK_CONDITION = CLOCKS_PER_BAUD * 12,
// CLOCKS_PER_HALF_BAUD = CLOCKS_PER_BAUD/2;
parameter INITIAL_SETUP = 30'd868;
// 8 data bits, no parity, (at least 1) stop bit
input i_clk, i_reset;
input [29:0] i_setup;
input i_uart;
input i_uart_rx;
output reg o_wr;
output reg [7:0] o_data;
output reg o_break;
131,6 → 129,7
wire [1:0] data_bits;
wire use_parity, parity_even, dblstop, fixd_parity;
reg [29:0] r_setup;
 
assign clocks_per_baud = { 4'h0, r_setup[23:0] };
assign data_bits = r_setup[29:28];
assign dblstop = r_setup[27];
138,8 → 137,15
assign fixd_parity = r_setup[25];
assign parity_even = r_setup[24];
assign break_condition = { r_setup[23:0], 4'h0 };
assign half_baud = { 5'h00, r_setup[23:1] };
assign half_baud = { 5'h00, r_setup[23:1] }-28'h1;
reg [27:0] baud_counter;
reg zero_baud_counter;
 
 
// Since this is an asynchronous receiver, we need to register our
// input a couple of clocks over to avoid any problems with
// metastability. We do that here, and then ignore all but the
// ck_uart wire.
reg q_uart, qq_uart, ck_uart;
initial q_uart = 1'b0;
initial qq_uart = 1'b0;
146,12 → 152,19
initial ck_uart = 1'b0;
always @(posedge i_clk)
begin
q_uart <= i_uart;
q_uart <= i_uart_rx;
qq_uart <= q_uart;
ck_uart <= qq_uart;
end
 
// In case anyone else wants this clocked, stabilized value, we
// offer it on our output.
assign o_ck_uart = ck_uart;
 
// Keep track of the number of clocks since the last change.
//
// This is used to determine if we are in either a break or an idle
// condition, as discussed further below.
reg [27:0] chg_counter;
initial chg_counter = 28'h00;
always @(posedge i_clk)
162,76 → 175,100
else if (chg_counter < break_condition)
chg_counter <= chg_counter + 1;
 
reg line_synch;
initial line_synch = 1'b0;
// Are we in a break condition?
//
// A break condition exists if the line is held low for longer than
// a data word. Hence, we keep track of when the last change occurred.
// If it was more than break_condition clocks ago, and the current input
// value is a 0, then we're in a break--and nothing can be read until
// the line idles again.
initial o_break = 1'b0;
always @(posedge i_clk)
o_break <= ((chg_counter >= break_condition)&&(~ck_uart))? 1'b1:1'b0;
 
// Are we between characters?
//
// The opposite of a break condition is where the line is held high
// for more clocks than would be in a character. When this happens,
// we know we have synchronization--otherwise, we might be sampling
// from within a data word.
//
// This logic is used later to hold the RXUART in a reset condition
// until we know we are between data words. At that point, we should
// be able to hold on to our synchronization.
reg line_synch;
initial line_synch = 1'b0;
always @(posedge i_clk)
line_synch <= ((chg_counter >= break_condition)&&(ck_uart));
 
// Are we in the middle of a baud iterval? Specifically, are we
// in the middle of a start bit? Set this to high if so. We'll use
// this within our state machine to transition out of the IDLE
// state.
reg half_baud_time;
initial half_baud_time = 0;
always @(posedge i_clk)
half_baud_time <= (~ck_uart)&&(chg_counter >= half_baud);
 
 
// Allow our controlling processor to change our setup at any time
// outside of receiving/processing a character.
initial r_setup = INITIAL_SETUP;
always @(posedge i_clk)
if (state >= `RXU_RESET_IDLE)
r_setup <= i_setup;
 
 
// Our monster state machine. YIKES!
//
// Yeah, this may be more complicated than it needs to be. The basic
// progression is:
// RESET -> RESET_IDLE -> (when line is idle) -> IDLE
// IDLE -> bit 0 -> bit 1 -> bit_{ndatabits} ->
// (optional) PARITY -> STOP -> (optional) SECOND_STOP
// -> IDLE
// ANY -> (on break) BREAK -> IDLE
//
// There are 16 states, although all are not used. These are listed
// at the top of this file.
//
// Logic inputs (12): (I've tried to minimize this number)
// state (4)
// i_reset
// line_synch
// o_break
// ckuart
// half_baud_time
// zero_baud_counter
// use_parity
// dblstop
// Logic outputs (4):
// state
//
reg [3:0] state;
reg [27:0] baud_counter;
reg [7:0] data_reg;
reg calc_parity, zero_baud_counter, half_baud_time;
initial o_wr = 1'b0;
initial state = `RXU_RESET_IDLE;
initial o_parity_err = 1'b0;
initial o_frame_err = 1'b0;
// initial baud_counter = clocks_per_baud;
always @(posedge i_clk)
begin
if (i_reset)
begin
o_wr <= 1'b0;
o_data <= 8'h00;
state <= `RXU_RESET_IDLE;
baud_counter <= clocks_per_baud-28'h01;// Set, not reset
data_reg <= 8'h00;
calc_parity <= 1'b0;
o_parity_err <= 1'b0;
o_frame_err <= 1'b0;
end else if (state == `RXU_RESET_IDLE)
else if (state == `RXU_RESET_IDLE)
begin
r_setup <= i_setup;
data_reg <= 8'h00; o_data <= 8'h00; o_wr <= 1'b0;
baud_counter <= clocks_per_baud-28'h01;// Set, not reset
if (line_synch)
// Goto idle state from a reset
state <= `RXU_IDLE;
else // Otherwise, stay in this condition 'til reset
state <= `RXU_RESET_IDLE;
calc_parity <= 1'b0;
o_parity_err <= 1'b0;
o_frame_err <= 1'b0;
end else if (o_break)
begin // We are in a break condition
state <= `RXU_BREAK;
o_wr <= 1'b0;
o_data <= 8'h00;
baud_counter <= clocks_per_baud-28'h01;// Set, not reset
data_reg <= 8'h00;
calc_parity <= 1'b0;
o_parity_err <= 1'b0;
o_frame_err <= 1'b0;
r_setup <= i_setup;
end else if (state == `RXU_BREAK)
begin // Goto idle state following return ck_uart going high
data_reg <= 8'h00; o_data <= 8'h00; o_wr <= 1'b0;
baud_counter <= clocks_per_baud - 28'h01;
if (ck_uart)
state <= `RXU_IDLE;
else
state <= `RXU_BREAK;
calc_parity <= 1'b0;
o_parity_err <= 1'b0;
o_frame_err <= 1'b0;
r_setup <= i_setup;
end else if (state == `RXU_IDLE)
begin // Idle state, independent of baud counter
r_setup <= i_setup;
data_reg <= 8'h00; o_data <= 8'h00; o_wr <= 1'b0;
baud_counter <= clocks_per_baud - 28'h01;
if ((~ck_uart)&&(half_baud_time))
begin
// We are in the center of a valid start bit
243,88 → 280,179
endcase
end else // Otherwise, just stay here in idle
state <= `RXU_IDLE;
calc_parity <= 1'b0;
o_parity_err <= 1'b0;
o_frame_err <= 1'b0;
end else if (zero_baud_counter)
begin
baud_counter <= clocks_per_baud-28'h1;
if (state < `RXU_BIT_SEVEN)
begin
// Data arrives least significant bit first.
// By the time this is clocked in, it's what
// you'll have.
data_reg <= { ck_uart, data_reg[7:1] };
calc_parity <= calc_parity ^ ck_uart;
o_data <= 8'h00;
o_wr <= 1'b0;
state <= state + 1;
o_parity_err <= 1'b0;
o_frame_err <= 1'b0;
end else if (state == `RXU_BIT_SEVEN)
begin
data_reg <= { ck_uart, data_reg[7:1] };
calc_parity <= calc_parity ^ ck_uart;
o_data <= 8'h00;
o_wr <= 1'b0;
else if (state == `RXU_BIT_SEVEN)
state <= (use_parity) ? `RXU_PARITY:`RXU_STOP;
o_parity_err <= 1'b0;
o_frame_err <= 1'b0;
end else if (state == `RXU_PARITY)
begin
if (fixd_parity)
o_parity_err <= (ck_uart ^ parity_even);
else
o_parity_err <= ((parity_even && (calc_parity != ck_uart))
||((~parity_even)&&(calc_parity==ck_uart)));
else if (state == `RXU_PARITY)
state <= `RXU_STOP;
o_frame_err <= 1'b0;
end else if (state == `RXU_STOP)
else if (state == `RXU_STOP)
begin // Stop (or parity) bit(s)
case (data_bits)
2'b00: o_data <= data_reg;
2'b01: o_data <= { 1'b0, data_reg[7:1] };
2'b10: o_data <= { 2'b0, data_reg[7:2] };
2'b11: o_data <= { 3'b0, data_reg[7:3] };
endcase
o_wr <= 1'b1; // Pulse the write
o_frame_err <= (~ck_uart);
if (~ck_uart)
if (~ck_uart) // On frame error, wait 4 ch idle
state <= `RXU_RESET_IDLE;
else if (dblstop)
state <= `RXU_SECOND_STOP;
else
state <= `RXU_IDLE;
// o_parity_err <= 1'b0;
end else // state must equal RX_SECOND_STOP
begin
if (~ck_uart)
begin
o_frame_err <= 1'b1;
if (~ck_uart) // On frame error, wait 4 ch idle
state <= `RXU_RESET_IDLE;
end else begin
else
state <= `RXU_IDLE;
o_frame_err <= 1'b0;
end
o_parity_err <= 1'b0;
end
end else begin
o_wr <= 1'b0; // data_reg = data_reg
baud_counter <= baud_counter - 28'd1;
o_parity_err <= 1'b0;
o_frame_err <= 1'b0;
end
end
 
// Data bit capture logic.
//
// This is drastically simplified from the state machine above, based
// upon: 1) it doesn't matter what it is until the end of a captured
// byte, and 2) the data register will flush itself of any invalid
// data in all other cases. Hence, let's keep it real simple.
// The only trick, though, is that if we have parity, then the data
// register needs to be held through that state without getting
// updated.
reg [7:0] data_reg;
always @(posedge i_clk)
if ((zero_baud_counter)&&(state != `RXU_PARITY))
data_reg <= { ck_uart, data_reg[7:1] };
 
// Parity calculation logic
//
// As with the data capture logic, all that must be known about this
// bit is that it is the exclusive-OR of all bits prior. The first
// of those will follow idle, so we set ourselves to zero on idle.
// Then, as we walk through the states of a bit, all will adjust this
// value up until the parity bit, where the value will be read. Setting
// it then or after will be irrelevant, so ... this should be good
// and simplified. Note--we don't need to adjust this on reset either,
// since the reset state will lead to the idle state where we'll be
// reset before any transmission takes place.
reg calc_parity;
always @(posedge i_clk)
if (state == `RXU_IDLE)
calc_parity <= 0;
else if (zero_baud_counter)
calc_parity <= calc_parity ^ ck_uart;
 
// Parity error logic
//
// Set during the parity bit interval, read during the last stop bit
// interval, cleared on BREAK, RESET_IDLE, or IDLE states.
initial o_parity_err = 1'b0;
always @(posedge i_clk)
if ((zero_baud_counter)&&(state == `RXU_PARITY))
begin
if (fixd_parity)
// Fixed parity bit--independent of any dat
// value.
o_parity_err <= (ck_uart ^ parity_even);
else if (parity_even)
// Parity even: The XOR of all bits including
// the parity bit must be zero.
o_parity_err <= (calc_parity != ck_uart);
else
// Parity odd: the parity bit must equal the
// XOR of all the data bits.
o_parity_err <= (calc_parity == ck_uart);
end else if (state >= `RXU_BREAK)
o_parity_err <= 1'b0;
 
// Frame error determination
//
// For the purpose of this controller, a frame error is defined as a
// stop bit (or second stop bit, if so enabled) not being high midway
// through the stop baud interval. The frame error value is
// immediately read, so we can clear it under all other circumstances.
// Specifically, we want it clear in RXU_BREAK, RXU_RESET_IDLE, and
// most importantly in RXU_IDLE.
initial o_frame_err = 1'b0;
always @(posedge i_clk)
if ((zero_baud_counter)&&((state == `RXU_STOP)
||(state == `RXU_SECOND_STOP)))
o_frame_err <= (o_frame_err)||(~ck_uart);
else if ((zero_baud_counter)||(state >= `RXU_BREAK))
o_frame_err <= 1'b0;
 
// Our data bit logic doesn't need nearly the complexity of all that
// work above. Indeed, we only need to know if we are at the end of
// a stop bit, in which case we copy the data_reg into our output
// data register, o_data.
//
// We would also set o_wr to be true when this is the case, but ... we
// won't know if there is a frame error on the second stop bit for
// another baud interval yet. So, instead, we set up the logic so that
// we know on the next zero baud counter that we can write out. That's
// the purpose of pre_wr.
initial o_data = 8'h00;
reg pre_wr;
initial pre_wr = 1'b0;
always @(posedge i_clk)
if (i_reset)
begin
pre_wr <= 1'b0;
o_data <= 8'h00;
end else if ((zero_baud_counter)&&(state == `RXU_STOP))
begin
pre_wr <= 1'b1;
case (data_bits)
2'b00: o_data <= data_reg;
2'b01: o_data <= { 1'b0, data_reg[7:1] };
2'b10: o_data <= { 2'b0, data_reg[7:2] };
2'b11: o_data <= { 3'b0, data_reg[7:3] };
endcase
end else if ((zero_baud_counter)||(state == `RXU_IDLE))
pre_wr <= 1'b0;
 
// Create an output strobe, true for one clock only, once we know
// all we need to know. o_data will be set on the last baud interval,
// o_parity_err on the last parity baud interval (if it existed,
// cleared otherwise, so ... we should be good to go here.)
initial o_wr = 1'b0;
always @(posedge i_clk)
if ((zero_baud_counter)||(state == `RXU_IDLE))
o_wr <= (pre_wr)&&(!i_reset);
else
o_wr <= 1'b0;
 
// The baud counter
//
// This is used as a "clock divider" if you will, but the clock needs
// to be reset before any byte can be decoded. In all other respects,
// we set ourselves up for clocks_per_baud counts between baud
// intervals.
always @(posedge i_clk)
if (i_reset)
baud_counter <= clocks_per_baud-28'h01;
else if (zero_baud_counter)
baud_counter <= clocks_per_baud-28'h01;
else case(state)
`RXU_RESET_IDLE:baud_counter <= clocks_per_baud-28'h01;
`RXU_BREAK: baud_counter <= clocks_per_baud-28'h01;
`RXU_IDLE: baud_counter <= clocks_per_baud-28'h01;
default: baud_counter <= baud_counter-28'h01;
endcase
 
// zero_baud_counter
//
// Rather than testing whether or not (baud_counter == 0) within our
// (already too complicated) state transition tables, we use
// zero_baud_counter to pre-charge that test on the clock
// before--cleaning up some otherwise difficult timing dependencies.
initial zero_baud_counter = 1'b0;
always @(posedge i_clk)
if (state == `RXU_IDLE)
zero_baud_counter <= 1'b0;
else
zero_baud_counter <= (baud_counter == 28'h01);
 
initial half_baud_time = 0;
always @(posedge i_clk)
half_baud_time <= (~ck_uart)&&(chg_counter >= half_baud);
 
 
endmodule
 
 
/txuart.v
104,13 → 104,14
`define TXU_IDLE 4'hf
//
//
module txuart(i_clk, i_reset, i_setup, i_break, i_wr, i_data, o_uart, o_busy);
module txuart(i_clk, i_reset, i_setup, i_break, i_wr, i_data,o_uart_tx, o_busy);
parameter INITIAL_SETUP = 30'd868;
input i_clk, i_reset;
input [29:0] i_setup;
input i_break;
input i_wr;
input [7:0] i_data;
output reg o_uart;
output reg o_uart_tx;
output wire o_busy;
 
wire [27:0] clocks_per_baud, break_condition;
130,7 → 131,8
reg [7:0] lcl_data;
reg calc_parity, r_busy, zero_baud_counter;
 
initial o_uart = 1'b1;
initial r_setup = INITIAL_SETUP;
initial o_uart_tx = 1'b1;
initial r_busy = 1'b1;
initial state = `TXU_IDLE;
initial lcl_data= 8'h0;
140,7 → 142,7
begin
if (i_reset)
begin
o_uart <= 1'b1;
o_uart_tx <= 1'b1;
r_busy <= 1'b1;
state <= `TXU_IDLE;
lcl_data <= 8'h0;
147,7 → 149,7
calc_parity <= 1'b0;
end else if (i_break)
begin
o_uart <= 1'b0;
o_uart_tx <= 1'b0;
state <= `TXU_BREAK;
calc_parity <= 1'b0;
r_busy <= 1'b1;
158,7 → 160,7
begin
state <= `TXU_IDLE;
r_busy <= 1'b1;
o_uart <= 1'b1;
o_uart_tx <= 1'b1;
calc_parity <= 1'b0;
end else if (state == `TXU_IDLE) // STATE_IDLE
begin
167,7 → 169,7
calc_parity <= 1'b0;
if ((i_wr)&&(~r_busy))
begin // Immediately start us off with a start bit
o_uart <= 1'b0;
o_uart_tx <= 1'b0;
r_busy <= 1'b1;
case(data_bits)
2'b00: state <= `TXU_BIT_ZERO;
178,7 → 180,7
lcl_data <= i_data;
// baud_counter <= clocks_per_baud-28'h01;
end else begin // Stay in idle
o_uart <= 1'b1;
o_uart_tx <= 1'b1;
r_busy <= 0;
// lcl_data is irrelevant
// state <= state;
189,7 → 191,7
r_busy <= 1'b1;
if (state[3] == 0) // First 8 bits
begin
o_uart <= lcl_data[0];
o_uart_tx <= lcl_data[0];
calc_parity <= calc_parity ^ lcl_data[0];
if (state == `TXU_BIT_SEVEN)
state <= (use_parity)?`TXU_PARITY:`TXU_STOP;
200,12 → 202,12
begin
state <= `TXU_STOP;
if (fixd_parity)
o_uart <= parity_even;
o_uart_tx <= parity_even;
else
o_uart <= calc_parity^((parity_even)? 1'b1:1'b0);
o_uart_tx <= calc_parity^((parity_even)? 1'b1:1'b0);
end else if (state == `TXU_STOP)
begin // two stop bit(s)
o_uart <= 1'b1;
o_uart_tx <= 1'b1;
if (dblstop)
state <= `TXU_SECOND_STOP;
else
214,7 → 216,7
end else // `TXU_SECOND_STOP and default:
begin
state <= `TXU_IDLE; // Go back to idle
o_uart <= 1'b1;
o_uart_tx <= 1'b1;
// Still r_busy, since we need to wait
// for the baud clock to finish counting
// out this last bit.
231,9 → 233,11
begin
zero_baud_counter <= (baud_counter == 28'h01);
if ((i_reset)||(i_break))
begin
// Give ourselves 16 bauds before being ready
baud_counter <= break_condition;
else if (~zero_baud_counter)
zero_baud_counter <= 1'b0;
end else if (~zero_baud_counter)
baud_counter <= baud_counter - 28'h01;
else if (state == `TXU_BREAK)
// Give us two stop bits before becoming available
/ufifo.v
0,0 → 1,189
////////////////////////////////////////////////////////////////////////////////
//
// Filename: ufifo.v
//
// Project: wbuart32, a full featured UART with simulator
//
// Purpose:
//
// Creator: Dan Gisselquist, Ph.D.
// Gisselquist Technology, LLC
//
////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2015-2016, Gisselquist Technology, LLC
//
// This program is free software (firmware): you can redistribute it and/or
// modify it under the terms of the GNU General Public License as published
// by the Free Software Foundation, either version 3 of the License, or (at
// your option) any later version.
//
// 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. See the GNU General Public License
// for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program. (It's in the $(ROOT)/doc directory, run make with no
// target there if the PDF file isn't present.) If not, see
// <http://www.gnu.org/licenses/> for a copy.
//
// License: GPL, v3, as defined and found on www.gnu.org,
// http://www.gnu.org/licenses/gpl.html
//
//
////////////////////////////////////////////////////////////////////////////////
//
//
module ufifo(i_clk, i_rst, i_wr, i_data, i_rd, o_data,
o_empty_n, o_half_full, o_status, o_err);
parameter BW=8, LGFLEN=4;
input i_clk, i_rst;
input i_wr;
input [(BW-1):0] i_data;
input i_rd;
output wire [(BW-1):0] o_data;
output reg o_empty_n;
output wire o_half_full;
output wire [15:0] o_status;
output wire o_err;
 
localparam FLEN=(1<<LGFLEN);
 
reg [(BW-1):0] fifo[0:(FLEN-1)];
reg [(LGFLEN-1):0] r_first, r_last;
 
wire [(LGFLEN-1):0] w_first_plus_one, w_first_plus_two,
w_last_plus_one;
assign w_first_plus_two = r_first + {{(LGFLEN-2){1'b0}},2'b10};
assign w_first_plus_one = r_first + {{(LGFLEN-1){1'b0}},1'b1};
assign w_last_plus_one = r_last + {{(LGFLEN-1){1'b0}},1'b1};
 
reg will_overflow;
initial will_overflow = 1'b0;
always @(posedge i_clk)
if (i_rst)
will_overflow <= 1'b0;
else if (i_rd)
will_overflow <= (will_overflow)&&(i_wr);
else if (i_wr)
will_overflow <= (w_first_plus_two == r_last);
else if (w_first_plus_one == r_last)
will_overflow <= 1'b1;
 
// Write
reg r_ovfl;
initial r_first = 0;
initial r_ovfl = 0;
always @(posedge i_clk)
if (i_rst)
begin
r_ovfl <= 1'b0;
r_first <= { (LGFLEN){1'b0} };
end else if (i_wr)
begin // Cowardly refuse to overflow
if ((i_rd)||(!will_overflow)) // (r_first+1 != r_last)
r_first <= w_first_plus_one;
else
r_ovfl <= 1'b1;
end
always @(posedge i_clk)
if (i_wr) // Write our new value regardless--on overflow or not
fifo[r_first] <= i_data;
 
// Reads
// Following a read, the next sample will be available on the
// next clock
// Clock ReadCMD ReadAddr Output
// 0 0 0 fifo[0]
// 1 1 0 fifo[0]
// 2 0 1 fifo[1]
// 3 0 1 fifo[1]
// 4 1 1 fifo[1]
// 5 1 2 fifo[2]
// 6 0 3 fifo[3]
// 7 0 3 fifo[3]
reg will_underflow, r_unfl;
initial will_underflow = 1'b1;
always @(posedge i_clk)
if (i_rst)
will_underflow <= 1'b1;
else if (i_wr)
will_underflow <= (will_underflow)&&(i_rd);
else if (i_rd)
will_underflow <= (w_last_plus_one == r_first);
else
will_underflow <= (r_last == r_first);
 
initial r_unfl = 1'b0;
initial r_last = 0;
always @(posedge i_clk)
if (i_rst)
begin
r_last <= { (LGFLEN){1'b0} };
r_unfl <= 1'b0;
end else if (i_rd)
begin
if ((i_wr)||(!will_underflow)) // (r_first != r_last)
r_last <= w_last_plus_one;
// Last chases first
// Need to be prepared for a possible two
// reads in quick succession
// o_data <= fifo[r_last+1];
else
r_unfl <= 1'b1;
end
 
reg [7:0] fifo_here, fifo_next, r_data;
always @(posedge i_clk)
fifo_here <= fifo[r_last];
always @(posedge i_clk)
fifo_next <= fifo[r_last+{{(LGFLEN-1){1'b0}},1'b1}];
always @(posedge i_clk)
r_data <= i_data;
 
reg [1:0] osrc;
always @(posedge i_clk)
if (will_underflow)
// o_data <= i_data;
osrc <= 2'b00;
else if ((i_rd)&&(r_first == w_last_plus_one))
osrc <= 2'b01;
else if (i_rd)
osrc <= 2'b11;
else
osrc <= 2'b10;
assign o_data = (osrc[1]) ? ((osrc[0])?fifo_next:fifo_here) : r_data;
 
// wire [(LGFLEN-1):0] current_fill;
// assign current_fill = (r_first-r_last);
 
always @(posedge i_clk)
if (i_rst)
o_empty_n <= 1'b0;
else case({i_wr, i_rd})
2'b00: o_empty_n <= (r_first != r_last);
2'b11: o_empty_n <= (r_first != r_last);
2'b10: o_empty_n <= 1'b1;
2'b01: o_empty_n <= (r_first != w_last_plus_one);
endcase
 
reg [(LGFLEN-1):0] r_fill;
always @(posedge i_clk)
if (i_rst)
r_fill <= 0;
else if ((i_rd)&&(!i_wr))
r_fill <= r_first - r_last - 1'b1;
else if ((!i_rd)&&(i_wr))
r_fill <= r_first - r_last + 1'b1;
else
r_fill <= r_first - r_last;
assign o_half_full = r_fill[(LGFLEN-1)];
 
assign o_err = (r_ovfl) || (r_unfl);
 
wire [3:0] lglen;
assign lglen = LGFLEN;
assign o_status = { lglen, {(16-2-4-LGFLEN){1'b0}}, r_fill, o_half_full, o_empty_n };
endmodule
/wbuart-insert.v
62,7 → 62,7
//
wire rx_stb, rx_break, rx_perr, rx_ferr, ck_uart;
wire [7:0] rx_data_port;
rxuart rx(i_clk, 1'b0, uart_setup, i_rx,
rxuart #(UART_SETUP) rx(i_clk, 1'b0, uart_setup, i_rx,
rx_stb, rx_data_port, rx_break,
rx_perr, rx_ferr, ck_uart);
 
71,18 → 71,24
always @(posedge i_clk)
if (rx_stb)
begin
r_rx_data[11] <= rx_break;
r_rx_data[10] <= rx_ferr;
r_rx_data[ 9] <= rx_perr;
r_rx_data[11] <= (r_rx_data[11])||(rx_break);
r_rx_data[10] <= (r_rx_data[10])||(rx_ferr);
r_rx_data[ 9] <= (r_rx_data[ 9])||(rx_perr);
r_rx_data[7:0]<= rx_data_port;
end else if ((i_wb_stb)&&(i_wb_we)
&&(i_wb_addr == `UART_RX_ADDR))
begin
r_rx_data[11] <= (rx_break)&& (!i_wb_data[11]);
r_rx_data[10] <= (rx_ferr) && (!i_wb_data[10]);
r_rx_data[ 9] <= (rx_perr) && (!i_wb_data[ 9]);
end
always @(posedge i_clk)
if(((i_wb_stb)&&(~i_wb_we)&&(i_wb_addr == `UART_RX_ADDR))
||(rx_stb))
r_rx_data[8] <= !rx_stb;
assign o_cts = rx_stb;
assign o_cts = !r_rx_data[8];
assign rx_data = { 20'h00, r_rx_data };
assign rx_int = r_rx_data[8];
assign rx_int = !r_rx_data[8];
 
//
// Then the UART transmitter
91,7 → 97,7
reg [7:0] r_tx_data;
reg r_tx_stb, r_tx_break;
wire [31:0] tx_data;
txuart tx(i_clk, 1'b0, uart_setup,
txuart #(UART_SETUP) tx(i_clk, 1'b0, uart_setup,
r_tx_break, r_tx_stb, r_tx_data,
o_tx, tx_busy);
always @(posedge i_clk)
/wbuart.v
0,0 → 1,354
////////////////////////////////////////////////////////////////////////////////
//
// Filename: wbuart.v
//
// Project: wbuart32, a full featured UART with simulator
//
// Purpose: Unlilke wbuart-insert.v, this is a full blown wishbone core
// with integrated FIFO support to support the UART transmitter
// and receiver found within here. As a result, it's usage may be
// heavier on the bus than the insert, but it may also be more useful.
//
// Creator: Dan Gisselquist, Ph.D.
// Gisselquist Technology, LLC
//
////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2015-2016, Gisselquist Technology, LLC
//
// This program is free software (firmware): you can redistribute it and/or
// modify it under the terms of the GNU General Public License as published
// by the Free Software Foundation, either version 3 of the License, or (at
// your option) any later version.
//
// 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. See the GNU General Public License
// for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program. (It's in the $(ROOT)/doc directory, run make with no
// target there if the PDF file isn't present.) If not, see
// <http://www.gnu.org/licenses/> for a copy.
//
// License: GPL, v3, as defined and found on www.gnu.org,
// http://www.gnu.org/licenses/gpl.html
//
//
////////////////////////////////////////////////////////////////////////////////
//
//
`define UART_SETUP 2'b00
`define UART_FIFO 2'b01
`define UART_RXREG 2'b10
`define UART_TXREG 2'b11
module wbuart(i_clk, i_rst,
//
i_wb_cyc, i_wb_stb, i_wb_we, i_wb_addr, i_wb_data,
o_wb_stall, o_wb_ack, o_wb_data,
//
i_uart_rx, o_uart_tx,
// i_uart_rts, o_uart_cts, i_uart_dtr, o_uart_dts
//
o_uart_rx_int, o_uart_tx_int,
o_uart_rxfifo_int, o_uart_txfifo_int);
parameter INITIAL_SETUP = 30'd25, // 4MB 8N1, when using 100MHz clock
LGFLEN = 4;
//
input i_clk, i_rst;
// Wishbone inputs
input i_wb_cyc, i_wb_stb, i_wb_we;
input [1:0] i_wb_addr;
input [31:0] i_wb_data;
output wire o_wb_stall;
output reg o_wb_ack;
output reg [31:0] o_wb_data;
//
input i_uart_rx;
output wire o_uart_tx;
output wire o_uart_rx_int, o_uart_tx_int,
o_uart_rxfifo_int, o_uart_txfifo_int;
 
wire tx_busy;
 
//
// The UART setup parameters: bits per byte, stop bits, parity, and
// baud rate are all captured within this uart_setup register.
//
reg [29:0] uart_setup;
initial uart_setup = INITIAL_SETUP;
always @(posedge i_clk)
// Under wishbone rules, a write takes place any time i_wb_stb
// is high. If that's the case, and if the write was to the
// setup address, then set us up for the new parameters.
if ((i_wb_stb)&&(i_wb_addr == `UART_SETUP)&&(i_wb_we))
uart_setup[29:0] <= i_wb_data[29:0];
 
//
// First the UART receiver
//
 
// First the wires/registers this receiver depends upon
wire rx_stb, rx_break, rx_perr, rx_ferr, ck_uart;
wire [7:0] rx_uart_data;
reg rx_uart_reset;
 
// Here's our UART receiver. Basically, it accepts our setup wires,
// the UART input, a clock, and a reset line, and produces outputs:
// a stb (true when new data is ready), an 8-bit data out value
// valid when stb is high, a break value (true during a break cond.),
// and parity/framing error flags--also valid when stb is true.
rxuart #(INITIAL_SETUP) rx(i_clk, (i_rst)||(rx_uart_reset),
uart_setup, i_uart_rx,
rx_stb, rx_uart_data, rx_break,
rx_perr, rx_ferr, ck_uart);
// The real trick is ... now that we have this data, what do we do
// with it?
 
 
// We place it into a receiver FIFO.
//
// Here's the declarations for the wires it needs.
wire rx_empty_n, rx_fifo_err;
wire [7:0] rxf_wb_data;
wire [15:0] rxf_status;
reg rxf_wb_read;
//
// And here's the FIFO proper.
//
// Note that the FIFO will be cleared upon any reset: either if there's
// a UART break condition on the line, the receiver is in reset, or an
// external reset is issued.
//
// The FIFO accepts strobe and data from the receiver.
// We issue another wire to it (rxf_wb_read), true when we wish to read
// from the FIFO, and we get our data in rxf_wb_data. The FIFO outputs
// four status-type values: 1) is it non-empty, 2) is the FIFO over half
// full, 3) a 16-bit status register, containing info regarding how full
// the FIFO truly is, and 4) an error indicator.
ufifo #(.LGFLEN(LGFLEN))
rxfifo(i_clk, (i_rst)||(rx_break)||(rx_uart_reset),
rx_stb, rx_uart_data,
rxf_wb_read, rxf_wb_data,
(rx_empty_n), (o_uart_rxfifo_int),
rxf_status, rx_fifo_err);
 
// We produce four interrupts. One of the receive interrupts indicates
// whether or not the receive FIFO is non-empty. This should wake up
// the CPU.
assign o_uart_rx_int = !rx_empty_n;
 
// If the bus requests that we read from the receive FIFO, we need to
// tell this to the receive FIFO. Note that because we are using a
// clock here, the output from the receive FIFO will necessarily be
// delayed by an extra clock.
initial rxf_wb_read = 1'b0;
always @(posedge i_clk)
rxf_wb_read <= (i_wb_stb)&&(i_wb_addr[1:0]==`UART_RXREG)
&&(!i_wb_we);
 
// Now, let's deal with those RX UART errors: both the parity and frame
// errors. As you may recall, these are valid only when rx_stb is
// valid, so we need to hold on to them until the user reads them via
// a UART read request..
reg r_rx_perr, r_rx_ferr;
initial r_rx_perr = 1'b0;
initial r_rx_ferr = 1'b0;
always @(posedge i_clk)
if ((rx_uart_reset)||(rx_break))
begin
// Clear the error
r_rx_perr <= 1'b0;
r_rx_ferr <= 1'b0;
end else if ((i_wb_stb)
&&(i_wb_addr[1:0]==`UART_RXREG)&&(i_wb_we))
begin
// Reset the error lines if a '1' is ever written to
// them, otherwise leave them alone.
//
r_rx_perr <= (r_rx_perr)&&(~i_wb_data[9]);
r_rx_ferr <= (r_rx_ferr)&&(~i_wb_data[10]);
end else if (rx_stb)
begin
// On an rx_stb, capture any parity or framing error
// indications. These aren't kept with the data rcvd,
// but rather kept external to the FIFO. As a result,
// if you get a parity or framing error, you will never
// know which data byte it was associated with.
// For now ... that'll work.
r_rx_perr <= (r_rx_perr)||(rx_perr);
r_rx_ferr <= (r_rx_ferr)||(rx_ferr);
end
 
initial rx_uart_reset = 1'b1;
always @(posedge i_clk)
if ((i_rst)||((i_wb_stb)&&(i_wb_addr[1:0]==`UART_SETUP)&&(i_wb_we)))
// The receiver reset, always set on a master reset
// request.
rx_uart_reset <= 1'b1;
else if ((i_wb_stb)&&(i_wb_addr[1:0]==`UART_RXREG)&&(i_wb_we))
// Writes to the receive register will command a receive
// reset anytime bit[12] is set.
rx_uart_reset <= i_wb_data[12];
else
rx_uart_reset <= 1'b0;
 
// Finally, we'll construct a 32-bit value from these various wires,
// to be returned over the bus on any read. These include the data
// that would be read from the FIFO, an error indicator set upon
// reading from an empty FIFO, a break indicator, and the frame and
// parity error signals.
wire [31:0] wb_rx_data;
assign wb_rx_data = { 16'h00,
3'h0, rx_fifo_err,
rx_break, rx_ferr, r_rx_perr, !rx_empty_n,
rxf_wb_data};
 
//
// Then the UART transmitter
//
wire tx_empty_n, txf_half_full, txf_err;
wire [7:0] tx_data;
wire [15:0] txf_status;
reg r_tx_break, txf_wb_write, tx_uart_reset;
reg [7:0] txf_wb_data;
 
// Unlike the receiver which goes from RXUART -> UFIFO -> WB, the
// transmitter basically goes WB -> UFIFO -> TXUART. Hence, to build
// support for the transmitter, we start with the command to write data
// into the FIFO. In this case, we use the act of writing to the
// UART_TXREG address as our indication that we wish to write to the
// FIFO. Here, we create a write command line, and latch the data for
// the extra clock that it'll take so that the command and data can be
// both true on the same clock.
initial txf_wb_write = 1'b0;
always @(posedge i_clk)
begin
txf_wb_write <= (i_wb_stb)&&(i_wb_addr == `UART_TXREG)
&&(i_wb_we);
txf_wb_data <= i_wb_data[7:0];
end
 
// Transmit FIFO
//
// Most of this is just wire management. The TX FIFO is identical in
// implementation to the RX FIFO (theyre both UFIFOs), but the TX
// FIFO is fed from the WB and read by the transmitter. Some key
// differences to note: we reset the transmitter on any request for a
// break. We read from the FIFO any time the UART transmitter is idle.
// and ... we just set the values (above) for controlling writing into
// this.
ufifo #(.LGFLEN(LGFLEN))
txfifo(i_clk, (r_tx_break)||(tx_uart_reset),
txf_wb_write, txf_wb_data,
(~tx_busy)&&(tx_empty_n), tx_data,
tx_empty_n, txf_half_full, txf_status, txf_err);
// Let's grab two interrupts from the FIFO for the CPU.
// The first will be true any time the FIFO is empty.
assign o_uart_tx_int = !tx_empty_n;
// The second will be true any time the FIFO is less than half
// full, allowing us a change to always keep it (near) fully
// charged.
assign o_uart_txfifo_int = !txf_half_full;
 
// Break logic
//
// A break in a UART controller is any time the UART holds the line
// low for an extended period of time. Here, we capture the wb_data[9]
// wire, on writes, as an indication we wish to break. As long as you
// write unsigned characters to the interface, this will never be true
// unless you wish it to be true. Be aware, though, writing a valid
// value to the interface will bring it out of the break condition.
initial r_tx_break = 1'b0;
always @(posedge i_clk)
if (i_rst)
r_tx_break <= 1'b0;
else if ((i_wb_stb)&&(i_wb_addr[1:0]==`UART_TXREG)&&(i_wb_we))
r_tx_break <= i_wb_data[9];
 
// TX-Reset logic
//
// This is nearly identical to the RX reset logic above. Basically,
// any time someone writes to bit [12] the transmitter will go through
// a reset cycle. Keep bit [12] low, and everything will proceed as
// normal.
initial tx_uart_reset = 1'b1;
always @(posedge i_clk)
if((i_rst)||((i_wb_stb)&&(i_wb_addr == `UART_SETUP)&&(i_wb_we)))
tx_uart_reset <= 1'b1;
else if ((i_wb_stb)&&(i_wb_addr[1:0]==`UART_TXREG)&&(i_wb_we))
tx_uart_reset <= i_wb_data[12];
else
tx_uart_reset <= 1'b0;
 
// Finally, the UART transmitter module itself. Note that we haven't
// connected the reset wire. Transmitting is as simple as setting
// the stb value (here set to tx_empty_n) and the data. When these
// are both set on the same clock that tx_busy is low, the transmitter
// will move on to the next data byte. Really, the only thing magical
// here is that tx_empty_n wire--thus, if there's anything in the FIFO,
// we read it here. (You might notice above, we register a read any
// time (tx_empty_n) and (!tx_busy) are both true---the condition for
// starting to transmit a new byte.)
txuart #(INITIAL_SETUP) tx(i_clk, 1'b0, uart_setup,
r_tx_break, (tx_empty_n), tx_data,
o_uart_tx, tx_busy);
 
// Now that we are done with the chain, pick some wires for the user
// to read on any read of the transmit port.
//
// This port is different from reading from the receive port, since
// there are no side effects. (Reading from the receive port advances
// the receive FIFO, here only writing to the transmit port advances the
// transmit FIFO--hence the read values are free for ... whatever.)
// We choose here to provide information about the transmit FIFO
// (txf_err, txf_half_full, tx_empty_n), information about the current
// voltage on the line (o_uart_tx)--and even the voltage on the receive
// line (ck_uart), as well as our current setting of the break and
// whether or not we are actively transmitting.
wire [31:0] wb_tx_data;
assign wb_tx_data = { 16'h00,
1'h0, txf_half_full, tx_empty_n, txf_err,
ck_uart, o_uart_tx, r_tx_break, tx_busy,
txf_wb_data};
 
// Each of the FIFO's returns a 16 bit status value. This value tells
// us both how big the FIFO is, as well as how much of the FIFO is in
// use. Let's merge those two status words together into a word we
// can use when reading about the FIFO.
wire [31:0] wb_fifo_data;
assign wb_fifo_data = { txf_status, rxf_status };
 
// You may recall from above that reads take two clocks. Hence, we
// need to delay the address decoding for a clock until the data is
// ready. We do that here.
reg [1:0] r_wb_addr;
always @(posedge i_clk)
r_wb_addr <= i_wb_addr;
 
// Likewise, the acknowledgement is delayed by one clock.
reg r_wb_ack;
always @(posedge i_clk) // We'll ACK in two clocks
r_wb_ack <= i_wb_stb;
always @(posedge i_clk) // Okay, time to set the ACK
o_wb_ack <= r_wb_ack;
 
// Finally, set the return data. This data must be valid on the same
// clock o_wb_ack is high. On all other clocks, it is irrelelant--since
// no one cares, no one is reading it, it gets lost in the mux in the
// interconnect, etc. For this reason, we can just simplify our logic.
always @(posedge i_clk)
casez(r_wb_addr)
`UART_SETUP: o_wb_data <= { 2'b00, uart_setup };
`UART_FIFO: o_wb_data <= wb_fifo_data;
`UART_RXREG: o_wb_data <= wb_rx_data;
`UART_TXREG: o_wb_data <= wb_tx_data;
endcase
 
// This device never stalls. Sure, it takes two clocks, but they are
// pipelined, and nothing stalls that pipeline. (Creates FIFO errors,
// perhaps, but doesn't stall the pipeline.) Hence, we can just
// set this value to zero.
assign o_wb_stall = 1'b0;
 
endmodule

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.