2021-01-23 15:19:09 +00:00
///////////////////////////////////////////
// uart.sv
//
// Written: David_Harris@hmc.edu 21 January 2021
// Modified:
//
// Purpose: Universial Asynchronous Receiver/ Transmitter with FIFOs
// Emulates interface of Texas Instruments PC16550D
2021-02-08 04:21:55 +00:00
// https://media.digikey.com/pdf/Data%20Sheets/Texas%20Instruments%20PDFs/PC16550D.pdf
2023-06-14 15:47:01 +00:00
// Compatible with UART in Imperas Virtio model
2021-01-23 15:19:09 +00:00
//
// Compatible with most of PC16550D with the following known exceptions:
// Generates 2 rather than 1.5 stop bits when 5-bit word length is slected and LCR[2] = 1
2023-06-14 15:47:01 +00:00
// Timeout not yet implemented
2021-01-23 15:19:09 +00:00
//
2023-01-14 14:15:35 +00:00
// Documentation: RISC-V System on Chip Design Chapter 15
//
2023-01-11 23:15:08 +00:00
// A component of the CORE-V-WALLY configurable RISC-V project.
2024-01-29 13:38:11 +00:00
// https://github.com/openhwgroup/cvw
2021-01-23 15:19:09 +00:00
//
2023-01-10 19:35:20 +00:00
// Copyright (C) 2021-23 Harvey Mudd College & Oklahoma State University
2021-01-23 15:19:09 +00:00
//
2023-01-10 19:35:20 +00:00
// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1
2021-01-23 15:19:09 +00:00
//
2023-01-10 19:35:20 +00:00
// Licensed under the Solderpad Hardware License v 2.1 (the “License”); you may not use this file
// except in compliance with the License, or, at your option, the Apache License version 2.0. You
// may obtain a copy of the License at
2021-01-23 15:19:09 +00:00
//
2023-01-10 19:35:20 +00:00
// https://solderpad.org/licenses/SHL-2.1/
//
// Unless required by applicable law or agreed to in writing, any work distributed under the
// License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
// either express or implied. See the License for the specific language governing permissions
// and limitations under the License.
2022-01-07 12:58:40 +00:00
////////////////////////////////////////////////////////////////////////////////////////////////
2021-01-23 15:19:09 +00:00
2021-12-15 16:25:08 +00:00
/* verilator lint_off UNOPTFLAT */
2021-01-23 15:19:09 +00:00
2023-06-14 15:39:01 +00:00
module uartPC16550D # ( parameter UART_PRESCALE ) (
2023-03-24 22:32:25 +00:00
// Processor Interface
input logic PCLK , PRESETn , // UART clock and active low reset
input logic [ 2 : 0 ] A , // address input (8 registers)
input logic [ 7 : 0 ] Din , // 8-bit WriteData
output logic [ 7 : 0 ] Dout , // 8-bit ReadData
input logic MEMRb , MEMWb , // Active low memory read/write
output logic INTR , TXRDYb , RXRDYb , // interrupt and ready lines
// Clocks
output logic BAUDOUTb , // active low baud clock
2023-06-15 17:20:29 +00:00
input logic RCLK , // usually BAUDOUTb tied to RCLK externally
2023-03-24 22:32:25 +00:00
// E1A Driver
input logic SIN , DSRb , DCDb , CTSb , RIb , // UART external serial and flow-control inputs
output logic SOUT , RTSb , DTRb , OUT1b , OUT2b // UART external serial and flow-control outputs
2023-01-15 01:07:36 +00:00
) ;
2021-01-23 15:19:09 +00:00
2023-01-15 01:07:36 +00:00
// transmit and receive states
2022-02-11 01:15:16 +00:00
typedef enum logic [ 1 : 0 ] { UART_IDLE , UART_ACTIVE , UART_DONE , UART_BREAK } statetype ;
2021-01-23 15:19:09 +00:00
// Registers
2023-01-21 00:47:36 +00:00
logic [ 10 : 0 ] RBR ;
logic [ 7 : 0 ] FCR , LCR , LSR , SCR , DLL , DLM ;
logic [ 3 : 0 ] IER , MSR ;
logic [ 4 : 0 ] MCR ;
2021-01-23 15:19:09 +00:00
// Syncrhonized and delayed UART signals
2023-03-24 22:32:25 +00:00
logic SINd , DSRbd , DCDbd , CTSbd , RIbd ;
logic SINsync , DSRbsync , DCDbsync , CTSbsync , RIbsync ;
logic DSRb2 , DCDb2 , CTSb2 , RIb2 ;
logic SOUTbit ;
2021-01-23 15:19:09 +00:00
// Control signals
2023-03-24 22:32:25 +00:00
logic loop ; // loopback mode
logic DLAB ; // Divisor Latch Access Bit (LCR bit 7)
2021-01-23 15:19:09 +00:00
// Baud and rx/tx timing
2023-06-15 17:20:29 +00:00
logic baudpulse , txbaudpulse , rxbaudpulse ; // high one system clk cycle each baud/16 period
logic [ 16 + UART_PRESCALE - 1 : 0 ] baudcount ;
logic [ 3 : 0 ] rxoversampledcnt , txoversampledcnt ; // count oversampled-by-16
2023-03-24 22:32:25 +00:00
logic [ 3 : 0 ] rxbitsreceived , txbitssent ;
statetype rxstate , txstate ;
2021-01-23 15:19:09 +00:00
// shift registrs and FIFOs
2023-03-24 22:32:25 +00:00
logic [ 9 : 0 ] rxshiftreg ;
logic [ 10 : 0 ] rxfifo [ 15 : 0 ] ;
logic [ 7 : 0 ] txfifo [ 15 : 0 ] ;
logic [ 4 : 0 ] rxfifotailunwrapped ;
logic [ 3 : 0 ] rxfifohead , rxfifotail , txfifohead , txfifotail , rxfifotriggerlevel ;
logic [ 3 : 0 ] rxfifoentries , txfifoentries ;
logic [ 3 : 0 ] rxbitsexpected , txbitsexpected ;
2021-01-23 15:19:09 +00:00
// receive data
2023-03-24 22:32:25 +00:00
logic [ 10 : 0 ] RXBR ;
logic [ 9 : 0 ] rxtimeoutcnt ;
logic rxcentered ;
logic rxparity , rxparitybit , rxstopbit ;
logic rxparityerr , rxoverrunerr , rxframingerr , rxbreak , rxfifohaserr ;
logic rxdataready ;
logic rxfifoempty , rxfifotriggered , rxfifotimeout ;
logic rxfifodmaready ;
logic [ 8 : 0 ] rxdata9 ;
logic [ 7 : 0 ] rxdata ;
logic [ 15 : 0 ] RXerrbit , rxfullbit ;
logic [ 31 : 0 ] rxfullbitunwrapped ;
2021-01-23 15:19:09 +00:00
// transmit data
2023-03-24 22:32:25 +00:00
logic [ 7 : 0 ] TXHR , nexttxdata ;
logic [ 11 : 0 ] txdata , txsr ;
logic txnextbit , txhrfull , txsrfull ;
logic txparity ;
logic txfifoempty , txfifofull , txfifodmaready ;
2021-01-23 15:19:09 +00:00
// control signals
2023-03-24 22:32:25 +00:00
logic fifoenabled , fifodmamodesel , evenparitysel ;
2021-01-23 15:19:09 +00:00
// interrupts
2023-03-24 22:32:25 +00:00
logic RXerr , RXerrIP , squashRXerrIP , prevSquashRXerrIP , setSquashRXerrIP , resetSquashRXerrIP ;
logic THRE , THRE_IP , squashTHRE_IP , prevSquashTHRE_IP , setSquashTHRE_IP , resetSquashTHRE_IP ;
logic rxdataavailintr , modemstatusintr , intrpending ;
logic [ 2 : 0 ] intrID ;
2021-01-23 15:19:09 +00:00
2023-03-24 22:32:25 +00:00
logic baudpulseComb ;
logic HeadPointerLastMove ;
2021-11-21 04:42:47 +00:00
2021-01-23 15:19:09 +00:00
///////////////////////////////////////////
// Input synchronization: 2-stage synchronizer
///////////////////////////////////////////
2023-06-15 17:20:29 +00:00
2022-07-06 13:26:14 +00:00
always_ff @ ( posedge PCLK ) begin
2021-04-24 13:32:09 +00:00
{ SINd , DSRbd , DCDbd , CTSbd , RIbd } < = # 1 { SIN , DSRb , DCDb , CTSb , RIb } ;
{ SINsync , DSRbsync , DCDbsync , CTSbsync , RIbsync } < = # 1 loop ? { SOUTbit , ~ MCR [ 0 ] , ~ MCR [ 3 ] , ~ MCR [ 1 ] , ~ MCR [ 2 ] } :
2023-03-24 22:32:25 +00:00
{ SINd , DSRbd , DCDbd , CTSbd , RIbd } ; // syncrhonized signals, handle loopback testing
2021-04-24 13:32:09 +00:00
{ DSRb2 , DCDb2 , CTSb2 , RIb2 } < = # 1 { DSRbsync , DCDbsync , CTSbsync , RIbsync } ; // for detecting state changes
2021-01-23 15:19:09 +00:00
end
///////////////////////////////////////////
// Register interface (Table 1, note some are read only and some write only)
///////////////////////////////////////////
2023-06-15 17:20:29 +00:00
2022-07-06 13:26:14 +00:00
always_ff @ ( posedge PCLK , negedge PRESETn )
if ( ~ PRESETn ) begin // Table 3 Reset Configuration
2021-04-24 13:32:09 +00:00
IER < = # 1 4 'b0 ;
FCR < = # 1 8 'b0 ;
2023-06-15 20:39:51 +00:00
LCR < = # 1 8 'b11 ; // **** fpga used to require reset to 3, double check this is no longer needed.
2021-04-24 13:32:09 +00:00
MCR < = # 1 5 'b0 ;
LSR < = # 1 8 'b01100000 ;
MSR < = # 1 4 'b0 ;
2023-03-24 22:32:25 +00:00
DLL < = # 1 8 'd1 ; // this cannot be zero with DLM also zer0.
DLM < = # 1 8 'b0 ;
2021-04-24 13:32:09 +00:00
SCR < = # 1 8 'b0 ; // not strictly necessary to reset
2021-01-23 15:19:09 +00:00
end else begin
if ( ~ MEMWb ) begin
2022-07-22 03:35:46 +00:00
/* verilator lint_off CASEINCOMPLETE */
2021-01-23 15:19:09 +00:00
case ( A )
2023-01-16 02:23:09 +00:00
3 'b000 : if ( DLAB ) DLL < = # 1 Din ; // else TXHR <= #1 Din; // TX handled in TX register/FIFO section
3 'b001 : if ( DLAB ) DLM < = # 1 Din ; else IER < = # 1 Din [ 3 : 0 ] ;
2021-04-24 13:32:09 +00:00
3 'b010 : FCR < = # 1 { Din [ 7 : 6 ] , 2 'b0 , Din [ 3 ] , 2 'b0 , Din [ 0 ] } ; // Write only FIFO Control Register; 4:5 reserved and 2:1 self-clearing
3 'b011 : LCR < = # 1 Din ;
3 'b100 : MCR < = # 1 Din [ 4 : 0 ] ;
3 'b111 : SCR < = # 1 Din ;
2021-01-23 15:19:09 +00:00
endcase
2022-07-22 03:35:46 +00:00
/* verilator lint_on CASEINCOMPLETE */
2021-01-23 15:19:09 +00:00
end
2022-07-22 03:35:46 +00:00
2021-01-23 15:19:09 +00:00
// Line Status Register (8.6.3)
2022-07-22 03:35:46 +00:00
// Ben 6/9/21 I don't like how this is a register. A lot of the individual bits have clocked components, so this just adds unecessary delay.
if ( ~ MEMWb & ( A = = 3 'b101 ) )
LSR [ 6 : 1 ] < = # 1 Din [ 6 : 1 ] ; // recommended only for test, see 8.6.3
else begin
2023-06-15 17:20:29 +00:00
LSR [ 0 ] < = # 1 rxdataready ; // Data ready
LSR [ 1 ] < = # 1 ( LSR [ 1 ] | RXBR [ 10 ] ) & ~ squashRXerrIP ; ; // overrun error
LSR [ 2 ] < = # 1 ( LSR [ 2 ] | RXBR [ 9 ] ) & ~ squashRXerrIP ; // parity error
LSR [ 3 ] < = # 1 ( LSR [ 3 ] | RXBR [ 8 ] ) & ~ squashRXerrIP ; // framing error
LSR [ 4 ] < = # 1 ( LSR [ 4 ] | rxbreak ) & ~ squashRXerrIP ; // break indicator
LSR [ 5 ] < = # 1 THRE ; // THRE
LSR [ 6 ] < = # 1 ~ txsrfull & THRE ; // TEMT
2022-07-22 03:35:46 +00:00
if ( rxfifohaserr ) LSR [ 7 ] < = # 1 1 ; // any bits in FIFO have error
end
2021-01-23 15:19:09 +00:00
// Modem Status Register (8.6.8)
2022-07-22 03:35:46 +00:00
if ( ~ MEMWb & ( A = = 3 'b110 ) )
MSR < = # 1 Din [ 3 : 0 ] ;
2022-07-22 04:29:27 +00:00
else if ( ~ MEMRb & ( A = = 3 'b110 ) )
MSR < = # 1 4 'b0 ; // Reading MSR clears the flags in MSR bits 3:0
2022-07-22 03:35:46 +00:00
else begin
MSR [ 0 ] < = # 1 MSR [ 0 ] | CTSb2 ^ CTSbsync ; // Delta Clear to Send
MSR [ 1 ] < = # 1 MSR [ 1 ] | DSRb2 ^ DSRbsync ; // Delta Data Set Ready
MSR [ 2 ] < = # 1 MSR [ 2 ] | ( ~ RIb2 & RIbsync ) ; // Trailing Edge of Ring Indicator
MSR [ 3 ] < = # 1 MSR [ 3 ] | DCDb2 ^ DCDbsync ; // Delta Data Carrier Detect
end
2021-01-23 15:19:09 +00:00
end
always_comb
if ( ~ MEMRb )
case ( A )
2021-06-10 00:58:20 +00:00
3 'b000 : if ( DLAB ) Dout = DLL ; else Dout = RBR [ 7 : 0 ] ;
2021-01-23 15:19:09 +00:00
3 'b001 : if ( DLAB ) Dout = DLM ; else Dout = { 4 'b0 , IER [ 3 : 0 ] } ;
2021-06-08 16:32:46 +00:00
3 'b010 : Dout = { { 2 { fifoenabled } } , 2 'b00 , intrID [ 2 : 0 ] , ~ intrpending } ; // Read only Interupt Ident Register
2021-01-23 15:19:09 +00:00
3 'b011 : Dout = LCR ;
3 'b100 : Dout = { 3 'b000 , MCR } ;
3 'b101 : Dout = LSR ;
2022-07-22 03:35:46 +00:00
// 3'b110: Dout = {~CTSbsync, ~DSRbsync, ~RIbsync, ~DCDbsync, MSR[3:0]};
3 'b110 : Dout = { ~ DCDbsync , ~ RIbsync , ~ DSRbsync , ~ CTSbsync , MSR [ 3 : 0 ] } ;
2021-01-23 15:19:09 +00:00
3 'b111 : Dout = SCR ;
endcase
else Dout = 8 'b0 ;
///////////////////////////////////////////
// Baud rate generator
// consider switching to same fixed-frequency reference clock used for TIME register
// prescale by factor of 2^UART_PRESCALE to allow for high-frequency reference clock
// Unlike PC16550D, this unit is hardwired with same rx and tx baud clock
2023-06-14 15:47:01 +00:00
// For example, with PCLK = 320 MHz, UART_PRESCALE = 5, DLM = 0, DLL = 65,
// 320 MHz system clock is divided by 65 x 2^5. The UART clock 16x oversamples
// the data, so the baud rate is 320x10^6 / (65 x 2^5 x 16) = 9615 Hz, which is
// close enough to 9600 baud to stay synchronized over the duration of one character.
2021-01-23 15:19:09 +00:00
///////////////////////////////////////////
2022-07-06 13:26:14 +00:00
always_ff @ ( posedge PCLK , negedge PRESETn )
if ( ~ PRESETn ) begin
2021-11-21 04:42:47 +00:00
baudcount < = # 1 1 ;
2021-04-24 13:32:09 +00:00
baudpulse < = # 1 0 ;
2022-01-02 21:47:21 +00:00
end else if ( ~ MEMWb & DLAB & ( A = = 3 'b0 | A = = 3 'b1 ) ) begin
2021-11-22 21:20:54 +00:00
baudcount < = # 1 1 ;
2021-01-23 15:19:09 +00:00
end else begin
2021-11-21 04:42:47 +00:00
// the baudpulse is too long by 2 clock cycles.
// This is cause baudpulse is registered adding 1 cycle and
// baudcount is reset when baudcount equals the threshold {DLM, DLL, UART_PRESCALE}
// rather than 1 less than that value. Alternatively the reset value could be 1 rather
// than 0.
baudpulse < = # 1 baudpulseComb ;
baudcount < = # 1 baudpulseComb ? 1 : baudcount + 1 ;
2021-01-23 15:19:09 +00:00
end
2021-11-21 04:42:47 +00:00
2023-05-26 21:24:12 +00:00
assign baudpulseComb = ( baudcount = = { DLM , DLL , { ( UART_PRESCALE ) { 1 'b0 } } } ) ;
2021-11-21 04:42:47 +00:00
2021-01-23 15:19:09 +00:00
assign txbaudpulse = baudpulse ;
assign BAUDOUTb = ~ baudpulse ;
assign rxbaudpulse = ~ RCLK ; // usually BAUDOUTb tied to RCLK externally
///////////////////////////////////////////
// receive timing and control
///////////////////////////////////////////
2023-06-15 17:20:29 +00:00
2022-07-06 13:26:14 +00:00
always_ff @ ( posedge PCLK , negedge PRESETn )
if ( ~ PRESETn ) begin
2023-06-15 17:20:29 +00:00
rxoversampledcnt < = # 1 0 ;
rxstate < = # 1 UART_IDLE ;
rxbitsreceived < = # 1 0 ;
rxtimeoutcnt < = # 1 0 ;
2021-01-23 15:19:09 +00:00
end else begin
if ( rxstate = = UART_IDLE & ~ SINsync ) begin // got start bit
2023-06-15 17:20:29 +00:00
rxstate < = # 1 UART_ACTIVE ;
2021-04-24 13:32:09 +00:00
rxoversampledcnt < = # 1 0 ;
2023-06-15 17:20:29 +00:00
rxbitsreceived < = # 1 0 ;
2022-09-19 23:00:30 +00:00
if ( ~ rxfifotimeout ) rxtimeoutcnt < = # 1 0 ; // reset timeout when new character is arriving. Jacob Pease: Only if the timeout was not already reached. p.16 PC16550D.pdf
2021-01-23 15:19:09 +00:00
end else if ( rxbaudpulse & ( rxstate = = UART_ACTIVE ) ) begin
2021-04-24 13:32:09 +00:00
rxoversampledcnt < = # 1 rxoversampledcnt + 1 ; // 16x oversampled counter
if ( rxcentered ) rxbitsreceived < = # 1 rxbitsreceived + 1 ;
if ( rxbitsreceived = = rxbitsexpected ) rxstate < = # 1 UART_DONE ; // pulse rxdone for a cycle
2022-01-02 21:47:21 +00:00
end else if ( rxstate = = UART_DONE | rxstate = = UART_BREAK ) begin
2021-04-24 13:32:09 +00:00
if ( rxbreak & ~ SINsync ) rxstate < = # 1 UART_BREAK ;
else rxstate < = # 1 UART_IDLE ;
2021-01-23 15:19:09 +00:00
end
// timeout counting
2021-07-04 15:39:59 +00:00
if ( ~ MEMRb & A = = 3 'b000 & ~ DLAB ) rxtimeoutcnt < = # 1 0 ; // reset timeout on read
2023-06-14 15:47:01 +00:00
else if ( fifoenabled & ~ rxfifoempty & rxbaudpulse & ~ rxfifotimeout ) rxtimeoutcnt < = # 1 rxtimeoutcnt + 1 ; // may not be right
2021-01-23 15:19:09 +00:00
end
2023-06-14 15:39:01 +00:00
assign rxcentered = rxbaudpulse & ( rxoversampledcnt = = 4 'b1000 ) ; // implies rxstate = UART_ACTIVE
2022-01-05 16:41:17 +00:00
2021-06-10 00:58:20 +00:00
assign rxbitsexpected = 4 'd1 + ( 4 'd5 + { 2 'b00 , LCR [ 1 : 0 ] } ) + { 3 'b000 , LCR [ 3 ] } + 4 'd1 ; // start bit + data bits + (parity bit) + stop bit
2021-01-23 15:19:09 +00:00
///////////////////////////////////////////
// receive shift register, buffer register, FIFO
///////////////////////////////////////////
2023-06-15 17:20:29 +00:00
2022-07-06 13:26:14 +00:00
always_ff @ ( posedge PCLK , negedge PRESETn )
if ( ~ PRESETn ) rxshiftreg < = # 1 10 'b0000000001 ; // initialize so that there is a valid stop bit
2021-04-24 13:32:09 +00:00
else if ( rxcentered ) rxshiftreg < = # 1 { rxshiftreg [ 8 : 0 ] , SINsync } ; // capture bit
2021-01-23 15:19:09 +00:00
assign rxparitybit = rxshiftreg [ 1 ] ; // parity, if it exists, in bit 1 when all done
assign rxstopbit = rxshiftreg [ 0 ] ;
always_comb
case ( LCR [ 1 : 0 ] ) // check how many bits used. Grab all bits including possible parity
2022-02-22 03:34:08 +00:00
2 'b00 : rxdata9 = { 3 'b0 , rxshiftreg [ 1 ] , rxshiftreg [ 2 ] , rxshiftreg [ 3 ] , rxshiftreg [ 4 ] , rxshiftreg [ 5 ] , rxshiftreg [ 6 ] } ; // 5-bit character
2 'b01 : rxdata9 = { 2 'b0 , rxshiftreg [ 1 ] , rxshiftreg [ 2 ] , rxshiftreg [ 3 ] , rxshiftreg [ 4 ] , rxshiftreg [ 5 ] , rxshiftreg [ 6 ] , rxshiftreg [ 7 ] } ; // 6-bit
2 'b10 : rxdata9 = { 1 'b0 , rxshiftreg [ 1 ] , rxshiftreg [ 2 ] , rxshiftreg [ 3 ] , rxshiftreg [ 4 ] , rxshiftreg [ 5 ] , rxshiftreg [ 6 ] , rxshiftreg [ 7 ] , rxshiftreg [ 8 ] } ; // 7-bit
2 'b11 : rxdata9 = { rxshiftreg [ 1 ] , rxshiftreg [ 2 ] , rxshiftreg [ 3 ] , rxshiftreg [ 4 ] , rxshiftreg [ 5 ] , rxshiftreg [ 6 ] , rxshiftreg [ 7 ] , rxshiftreg [ 8 ] , rxshiftreg [ 9 ] } ; // 8-bit
2021-01-23 15:19:09 +00:00
endcase
2022-02-22 05:04:18 +00:00
assign rxdata = LCR [ 3 ] ? rxdata9 [ 7 : 0 ] : rxdata9 [ 8 : 1 ] ; // discard parity bit
2021-01-23 15:19:09 +00:00
// ERROR CONDITIONS
2023-06-15 17:20:29 +00:00
assign rxparity = ^ rxdata ;
assign rxparityerr = ( rxparity ^ rxparitybit ^ ~ evenparitysel ) & LCR [ 3 ] ; // Check even/odd parity (*** check if LCR needs to be inverted)
2021-01-23 15:19:09 +00:00
assign rxoverrunerr = fifoenabled ? ( rxfifoentries = = 15 ) : rxdataready ; // overrun if FIFO or receive buffer register full
assign rxframingerr = ~ rxstopbit ; // framing error if no stop bit
2023-06-15 17:20:29 +00:00
assign rxbreak = rxframingerr & ( rxdata9 = = 9 'b0 ) ; // break when 0 for start + data + parity + stop time
2021-01-23 15:19:09 +00:00
// receive FIFO and register
2023-03-25 01:37:48 +00:00
always_ff @ ( posedge PCLK )
2022-07-06 13:26:14 +00:00
if ( ~ PRESETn ) begin
2021-04-24 13:32:09 +00:00
rxfifohead < = # 1 0 ; rxfifotail < = # 1 0 ; rxdataready < = # 1 0 ; RXBR < = # 1 0 ;
2021-01-23 15:19:09 +00:00
end else begin
2022-07-23 00:13:19 +00:00
if ( ~ MEMWb & ( A = = 3 'b010 ) & Din [ 1 ] ) begin
rxfifohead < = # 1 0 ; rxfifotail < = # 1 0 ; rxdataready < = # 1 0 ;
end else if ( rxstate = = UART_DONE ) begin
2021-04-24 13:32:09 +00:00
RXBR < = # 1 { rxoverrunerr , rxparityerr , rxframingerr , rxdata } ; // load recevive buffer register
2022-07-28 04:35:51 +00:00
if ( rxoverrunerr ) $warning ( " UART RX Overrun Err \n " ) ;
2023-06-15 17:20:29 +00:00
if ( rxparityerr ) $warning ( " UART RX Parity Err \n " ) ;
2022-07-28 04:35:51 +00:00
if ( rxframingerr ) $warning ( " UART RX Framing Err \n " ) ;
2021-01-23 15:19:09 +00:00
if ( fifoenabled ) begin
2021-06-10 13:53:48 +00:00
rxfifo [ rxfifohead ] < = # 1 { rxoverrunerr , rxparityerr , rxframingerr , rxdata } ;
2021-04-24 13:32:09 +00:00
rxfifohead < = # 1 rxfifohead + 1 ;
2021-01-23 15:19:09 +00:00
end
2021-04-24 13:32:09 +00:00
rxdataready < = # 1 1 ;
2021-07-04 15:39:59 +00:00
end else if ( ~ MEMRb & A = = 3 'b000 & ~ DLAB ) begin // reading RBR updates ready / pops fifo
2021-01-23 15:19:09 +00:00
if ( fifoenabled ) begin
2022-05-22 15:55:28 +00:00
if ( ~ rxfifoempty ) rxfifotail < = # 1 rxfifotail + 1 ;
2022-07-23 00:13:19 +00:00
// if (rxfifoempty) rxdataready <= #1 0;
if ( rxfifoentries = = 1 ) rxdataready < = # 1 0 ; // When reading the last entry, data ready becomes zero
2022-04-01 00:54:32 +00:00
end else begin
rxdataready < = # 1 0 ;
2022-04-01 20:20:45 +00:00
RXBR < = # 1 { 1 'b0 , RXBR [ 9 : 0 ] } ; // Ben 31 March 2022: I added this so that rxoverrunerr permanently goes away upon reading RBR (when not in FIFO mode)
2022-04-01 00:54:32 +00:00
end
2021-07-04 15:39:59 +00:00
end else if ( ~ MEMWb & A = = 3 'b010 ) // writes to FIFO Control Register
2021-01-23 15:19:09 +00:00
if ( Din [ 1 ] | ~ Din [ 0 ] ) begin // rx FIFO reset or FIFO disable clears FIFO contents
2021-04-24 13:32:09 +00:00
rxfifohead < = # 1 0 ; rxfifotail < = # 1 0 ;
2021-01-23 15:19:09 +00:00
end
end
assign rxfifoempty = ( rxfifohead = = rxfifotail ) ;
2021-06-10 00:58:20 +00:00
// verilator lint_off WIDTH
2021-01-23 15:19:09 +00:00
assign rxfifoentries = ( rxfifohead > = rxfifotail ) ? ( rxfifohead - rxfifotail ) :
2021-12-15 16:25:08 +00:00
( rxfifohead + 16 - rxfifotail ) ;
2021-06-10 00:58:20 +00:00
// verilator lint_on WIDTH
2021-01-23 15:19:09 +00:00
assign rxfifotriggered = rxfifoentries > = rxfifotriggerlevel ;
2023-06-14 15:47:01 +00:00
assign rxfifotimeout = rxtimeoutcnt = = { rxbitsexpected , 6 'b0 } ; // time out after 4 character periods; probably not right yet
2022-09-19 23:00:30 +00:00
//assign rxfifotimeout = 0; // disabled pending fix
2021-01-23 15:19:09 +00:00
// detect any errors in rx fifo
2021-01-27 11:40:26 +00:00
// although rxfullbit looks like a combinational loop, in one bit rxfifotail == i and breaks the loop
2021-09-13 00:00:24 +00:00
// tail is normally higher than head, but might wrap around. unwrapped variable adds 16 to eliminate wrapping
2022-01-05 16:41:17 +00:00
assign rxfifotailunwrapped = rxfifotail < rxfifohead ? { 1 'b1 , rxfifotail } : { 1 'b0 , rxfifotail } ;
genvar i ;
for ( i = 0 ; i < 32 ; i + + ) begin : rxfull
if ( i = = 0 ) assign rxfullbitunwrapped [ i ] = ( rxfifohead = = 0 ) & ( rxfifotail ! = 0 ) ;
else assign rxfullbitunwrapped [ i ] = ( { 1 'b0 , rxfifohead } = = i | rxfullbitunwrapped [ i - 1 ] ) & ( rxfifotailunwrapped ! = i ) ;
end
for ( i = 0 ; i < 16 ; i + + ) begin : rx
2023-06-15 17:20:29 +00:00
assign RXerrbit [ i ] = | rxfifo [ i ] [ 10 : 8 ] ; // are any of the error conditions set?
2022-01-05 16:41:17 +00:00
assign rxfullbit [ i ] = rxfullbitunwrapped [ i ] | rxfullbitunwrapped [ i + 16 ] ;
/ * if ( i > 0 )
assign rxfullbit [ i ] = ( ( rxfifohead = = i ) | rxfullbit [ i - 1 ] ) & ( rxfifotail ! = i ) ;
else
assign rxfullbit [ 0 ] = ( ( rxfifohead = = i ) | rxfullbit [ 15 ] ) & ( rxfifotail ! = i ) ; */
end
2023-06-15 17:20:29 +00:00
assign rxfifohaserr = | ( RXerrbit & rxfullbit ) ;
2021-01-23 15:19:09 +00:00
// receive buffer register and ready bit
2022-07-06 13:26:14 +00:00
always_ff @ ( posedge PCLK , negedge PRESETn ) // track rxrdy for DMA mode (FCR3 = FCR0 = 1)
if ( ~ PRESETn ) rxfifodmaready < = # 1 0 ;
2021-04-24 13:32:09 +00:00
else if ( rxfifotriggered | rxfifotimeout ) rxfifodmaready < = # 1 1 ;
else if ( rxfifoempty ) rxfifodmaready < = # 1 0 ;
2021-01-23 15:19:09 +00:00
always_comb
if ( fifoenabled ) begin
if ( rxfifoempty ) RBR = 11 'b0 ;
else RBR = rxfifo [ rxfifotail ] ;
if ( fifodmamodesel ) RXRDYb = ~ rxfifodmaready ;
else RXRDYb = rxfifoempty ;
end else begin
2023-06-15 17:20:29 +00:00
RBR = RXBR ;
2021-01-23 15:19:09 +00:00
RXRDYb = ~ rxdataready ;
end
///////////////////////////////////////////
2023-03-24 22:32:25 +00:00
// transmit timing and control
2021-01-23 15:19:09 +00:00
///////////////////////////////////////////
2023-06-15 17:20:29 +00:00
2022-07-06 13:26:14 +00:00
always_ff @ ( posedge PCLK , negedge PRESETn )
if ( ~ PRESETn ) begin
2021-04-24 13:32:09 +00:00
txoversampledcnt < = # 1 0 ;
2023-06-15 17:20:29 +00:00
txstate < = # 1 UART_IDLE ;
txbitssent < = # 1 0 ;
2021-07-04 15:39:59 +00:00
end else if ( ( txstate = = UART_IDLE ) & txsrfull ) begin // start transmitting
2023-06-15 17:20:29 +00:00
txstate < = # 1 UART_ACTIVE ;
2021-04-24 13:32:09 +00:00
txoversampledcnt < = # 1 1 ;
2023-06-15 17:20:29 +00:00
txbitssent < = # 1 0 ;
2021-01-23 15:19:09 +00:00
end else if ( txbaudpulse & ( txstate = = UART_ACTIVE ) ) begin
2021-04-24 13:32:09 +00:00
txoversampledcnt < = # 1 txoversampledcnt + 1 ;
2021-01-23 15:19:09 +00:00
if ( txnextbit ) begin // transmit at end of phase
2021-04-24 13:32:09 +00:00
txbitssent < = # 1 txbitssent + 1 ;
if ( txbitssent = = txbitsexpected ) txstate < = # 1 UART_DONE ;
2021-01-23 15:19:09 +00:00
end
end else if ( txstate = = UART_DONE ) begin
2021-04-24 13:32:09 +00:00
txstate < = # 1 UART_IDLE ;
2021-01-23 15:19:09 +00:00
end
2022-07-22 03:35:46 +00:00
assign txbitsexpected = 4 'd1 + ( 4 'd5 + { 2 'b00 , LCR [ 1 : 0 ] } ) + { 3 'b000 , LCR [ 3 ] } + 4 'd1 + { 3 'b000 , LCR [ 2 ] } - 4 'd1 ; // start bit + data bits + (parity bit) + stop bit(s) - 1
2023-06-14 15:39:01 +00:00
assign txnextbit = txbaudpulse & ( txoversampledcnt = = 4 'b0000 ) ; // implies txstate = UART_ACTIVE
2021-01-23 15:19:09 +00:00
/ //////////////////////////////////////////
// transmit holding register, shift register, FIFO
///////////////////////////////////////////
2023-06-15 17:20:29 +00:00
2021-01-23 15:19:09 +00:00
always_comb begin // compute value for parity and tx holding register
nexttxdata = fifoenabled ? txfifo [ txfifotail ] : TXHR ; // pick from FIFO or holding register
case ( LCR [ 1 : 0 ] ) // compute parity from appropriate number of bits
2023-06-14 15:47:01 +00:00
2 'b00 : txparity = ^ nexttxdata [ 4 : 0 ] ^ ~ evenparitysel ;
2021-01-23 15:19:09 +00:00
2 'b01 : txparity = ^ nexttxdata [ 5 : 0 ] ^ ~ evenparitysel ;
2 'b10 : txparity = ^ nexttxdata [ 6 : 0 ] ^ ~ evenparitysel ;
2 'b11 : txparity = ^ nexttxdata [ 7 : 0 ] ^ ~ evenparitysel ;
endcase
case ( { LCR [ 3 ] , LCR [ 1 : 0 ] } ) // parity, data bits
// load up start bit (0), 5-8 data bits, 0-1 parity bits, 2 stop bits (only one sometimes used), padding
2023-01-15 01:07:36 +00:00
3 'b000 : txdata = { 1 'b0 , nexttxdata [ 0 ] , nexttxdata [ 1 ] , nexttxdata [ 2 ] , nexttxdata [ 3 ] , nexttxdata [ 4 ] , 6 'b111111 } ; // 5 data, no parity
3 'b001 : txdata = { 1 'b0 , nexttxdata [ 0 ] , nexttxdata [ 1 ] , nexttxdata [ 2 ] , nexttxdata [ 3 ] , nexttxdata [ 4 ] , nexttxdata [ 5 ] , 5 'b11111 } ; // 6 data, no parity
3 'b010 : txdata = { 1 'b0 , nexttxdata [ 0 ] , nexttxdata [ 1 ] , nexttxdata [ 2 ] , nexttxdata [ 3 ] , nexttxdata [ 4 ] , nexttxdata [ 5 ] , nexttxdata [ 6 ] , 4 'b1111 } ; // 7 data, no parity
2021-11-21 04:42:47 +00:00
3 'b011 : txdata = { 1 'b0 , nexttxdata [ 0 ] , nexttxdata [ 1 ] , nexttxdata [ 2 ] , nexttxdata [ 3 ] , nexttxdata [ 4 ] , nexttxdata [ 5 ] , nexttxdata [ 6 ] , nexttxdata [ 7 ] , 3 'b111 } ; // 8 data, no parity
2023-01-15 01:07:36 +00:00
3 'b100 : txdata = { 1 'b0 , nexttxdata [ 0 ] , nexttxdata [ 1 ] , nexttxdata [ 2 ] , nexttxdata [ 3 ] , nexttxdata [ 4 ] , txparity , 5 'b11111 } ; // 5 data, parity
3 'b101 : txdata = { 1 'b0 , nexttxdata [ 0 ] , nexttxdata [ 1 ] , nexttxdata [ 2 ] , nexttxdata [ 3 ] , nexttxdata [ 4 ] , nexttxdata [ 5 ] , txparity , 4 'b1111 } ; // 6 data, parity
3 'b110 : txdata = { 1 'b0 , nexttxdata [ 0 ] , nexttxdata [ 1 ] , nexttxdata [ 2 ] , nexttxdata [ 3 ] , nexttxdata [ 4 ] , nexttxdata [ 5 ] , nexttxdata [ 6 ] , txparity , 3 'b111 } ; // 7 data, parity
2021-11-21 04:42:47 +00:00
3 'b111 : txdata = { 1 'b0 , nexttxdata [ 0 ] , nexttxdata [ 1 ] , nexttxdata [ 2 ] , nexttxdata [ 3 ] , nexttxdata [ 4 ] , nexttxdata [ 5 ] , nexttxdata [ 6 ] , nexttxdata [ 7 ] , txparity , 2 'b11 } ; // 8 data, parity
2021-01-23 15:19:09 +00:00
endcase
end
2021-12-15 16:25:08 +00:00
2021-01-23 15:19:09 +00:00
// registers & FIFO
2022-07-06 13:26:14 +00:00
always_ff @ ( posedge PCLK , negedge PRESETn )
if ( ~ PRESETn ) begin
2021-04-24 13:32:09 +00:00
txfifohead < = # 1 0 ; txfifotail < = # 1 0 ; txhrfull < = # 1 0 ; txsrfull < = # 1 0 ; TXHR < = # 1 0 ; txsr < = # 1 12 'hfff ;
2022-07-23 00:13:19 +00:00
end else if ( ~ MEMWb & ( A = = 3 'b010 ) & Din [ 2 ] ) begin
txfifohead < = # 1 0 ; txfifotail < = # 1 0 ;
2021-01-23 15:19:09 +00:00
end else begin
2021-07-04 15:39:59 +00:00
if ( ~ MEMWb & A = = 3 'b000 & ~ DLAB ) begin // writing transmit holding register or fifo
2021-01-23 15:19:09 +00:00
if ( fifoenabled ) begin
2021-04-24 13:32:09 +00:00
txfifo [ txfifohead ] < = # 1 Din ;
2023-06-15 17:20:29 +00:00
txfifohead < = # 1 txfifohead + 1 ;
2021-01-23 15:19:09 +00:00
end else begin
2023-06-15 17:20:29 +00:00
TXHR < = # 1 Din ;
2021-04-24 13:32:09 +00:00
txhrfull < = # 1 1 ;
2021-01-23 15:19:09 +00:00
end
2021-03-05 19:24:22 +00:00
$write ( " %c " , Din ) ; // for testbench
2021-12-15 16:25:08 +00:00
end
2021-02-12 05:02:45 +00:00
if ( txstate = = UART_IDLE ) begin // move data into tx shift register if available
if ( fifoenabled ) begin
2022-07-23 00:13:19 +00:00
if ( ~ txfifoempty & ~ txsrfull ) begin
2023-06-15 17:20:29 +00:00
txsr < = # 1 txdata ;
2021-04-24 13:32:09 +00:00
txfifotail < = # 1 txfifotail + 1 ;
2023-06-15 17:20:29 +00:00
txsrfull < = # 1 1 ;
2021-01-23 15:19:09 +00:00
end
2021-02-12 05:02:45 +00:00
end else if ( txhrfull ) begin
2023-06-15 17:20:29 +00:00
txsr < = # 1 txdata ;
2021-04-24 13:32:09 +00:00
txhrfull < = # 1 0 ;
txsrfull < = # 1 1 ;
2021-01-23 15:19:09 +00:00
end
2021-04-24 13:32:09 +00:00
end else if ( txstate = = UART_DONE ) txsrfull < = # 1 0 ; // done transmitting shift register
2021-07-04 15:39:59 +00:00
else if ( txstate = = UART_ACTIVE & txnextbit ) txsr < = # 1 { txsr [ 10 : 0 ] , 1 'b1 } ; // shift txhr
if ( ! MEMWb & A = = 3 'b010 ) // writes to FIFO control register
2021-01-23 15:19:09 +00:00
if ( Din [ 2 ] | ~ Din [ 0 ] ) begin // tx FIFO reste or FIFO disable clears FIFO contents
2021-04-24 13:32:09 +00:00
txfifohead < = # 1 0 ; txfifotail < = # 1 0 ;
2021-01-23 15:19:09 +00:00
end
end
2022-10-26 19:48:09 +00:00
always_ff @ ( posedge PCLK , negedge PRESETn ) begin
2023-03-24 22:32:25 +00:00
// special condition to check if the fifo is empty or full. Because the head
// pointer indicates where the next write goes and not the location of the
// current head, the head and tail pointer being equal imply two different
// things. First it could mean the fifo is empty and second it could mean
// the fifo is full. To differenciate we need to know which pointer moved
// to cause them to be equal. If the head pointer moved then it is full.
// If the tail pointer moved then it is empty. it resets to empty so
// if reset with the tail pointer indicating the last update.
if ( ~ PRESETn )
HeadPointerLastMove < = 1 'b0 ;
else if ( fifoenabled & ~ MEMWb & A = = 3 'b000 & ~ DLAB )
HeadPointerLastMove < = 1 'b1 ;
else if ( fifoenabled & ~ txfifoempty & ~ txsrfull & txstate = = UART_IDLE )
HeadPointerLastMove < = 1 'b0 ;
2022-10-26 19:48:09 +00:00
end
2023-06-15 17:20:29 +00:00
assign txfifoempty = ( txfifohead = = txfifotail ) & ~ HeadPointerLastMove ;
2021-06-10 00:58:20 +00:00
// verilator lint_off WIDTH
2021-01-23 15:19:09 +00:00
assign txfifoentries = ( txfifohead > = txfifotail ) ? ( txfifohead - txfifotail ) :
2021-12-15 16:25:08 +00:00
( txfifohead + 16 - txfifotail ) ;
2021-06-10 00:58:20 +00:00
// verilator lint_on WIDTH
2023-03-24 22:32:25 +00:00
assign txfifofull = ( txfifohead = = txfifotail ) & HeadPointerLastMove ;
2021-01-23 15:19:09 +00:00
// transmit buffer ready bit
2022-07-06 13:26:14 +00:00
always_ff @ ( posedge PCLK , negedge PRESETn ) // track txrdy for DMA mode (FCR3 = FCR0 = 1)
if ( ~ PRESETn ) txfifodmaready < = # 1 0 ;
2021-04-24 13:32:09 +00:00
else if ( txfifoempty ) txfifodmaready < = # 1 1 ;
else if ( txfifofull ) txfifodmaready < = # 1 0 ;
2021-01-23 15:19:09 +00:00
always_comb
if ( fifoenabled & fifodmamodesel ) TXRDYb = ~ txfifodmaready ;
2021-06-08 16:32:46 +00:00
else TXRDYb = ~ THRE ;
2021-01-23 15:19:09 +00:00
// Transmitter pin
2021-01-27 11:40:26 +00:00
assign SOUTbit = txsr [ 11 ] ; // transmit most significant bit
2023-06-15 17:20:29 +00:00
assign SOUT = loop ? 1 : ( LCR [ 6 ] ? 0 : SOUTbit ) ; // tied to 1 during loopback or 0 during break
2021-01-23 15:19:09 +00:00
///////////////////////////////////////////
// interrupts
///////////////////////////////////////////
2023-06-15 17:20:29 +00:00
2021-06-10 13:53:48 +00:00
assign RXerr = | LSR [ 4 : 1 ] ; // LS interrupt if any of the flags are true
assign RXerrIP = RXerr & ~ squashRXerrIP ; // intr squashed upon reading LSR
2021-01-23 15:19:09 +00:00
assign rxdataavailintr = fifoenabled ? rxfifotriggered : rxdataready ;
2021-06-10 13:53:48 +00:00
assign THRE = fifoenabled ? txfifoempty : ~ txhrfull ;
assign THRE_IP = THRE & ~ squashTHRE_IP ; // THRE_IP squashed upon reading IIR
2021-01-23 15:19:09 +00:00
assign modemstatusintr = | MSR [ 3 : 0 ] ; // set interrupt when modem pins change
2021-12-15 16:25:08 +00:00
2021-06-08 16:32:46 +00:00
// IIR: interrupt priority (Table 5)
// set intrID based on highest priority pending interrupt source; otherwise, no interrupt is pending
2021-01-23 15:19:09 +00:00
always_comb begin
intrpending = 1 ;
2021-06-10 13:53:48 +00:00
if ( RXerrIP & IER [ 2 ] ) intrID = 3 'b011 ;
else if ( rxdataavailintr & IER [ 0 ] ) intrID = 3 'b010 ;
else if ( rxfifotimeout & fifoenabled & IER [ 0 ] ) intrID = 3 'b110 ;
else if ( THRE_IP & IER [ 1 ] ) intrID = 3 'b001 ;
else if ( modemstatusintr & IER [ 3 ] ) intrID = 3 'b000 ;
2021-01-23 15:19:09 +00:00
else begin
2021-06-08 16:32:46 +00:00
intrID = 3 'b000 ;
2021-01-23 15:19:09 +00:00
intrpending = 0 ;
end
end
2022-07-06 13:26:14 +00:00
always @ ( posedge PCLK ) INTR < = # 1 intrpending ; // prevent glitches on interrupt pin
2021-01-23 15:19:09 +00:00
2021-06-10 13:53:48 +00:00
// Side effect of reading LSR is lowering overrun, parity, framing, break intr's
assign setSquashRXerrIP = ~ MEMRb & ( A = = 3 'b101 ) ;
assign resetSquashRXerrIP = ( rxstate = = UART_DONE ) ;
assign squashRXerrIP = ( prevSquashRXerrIP | setSquashRXerrIP ) & ~ resetSquashRXerrIP ;
2022-07-06 13:26:14 +00:00
flopr # ( 1 ) squashRXerrIPreg ( PCLK , ~ PRESETn , squashRXerrIP , prevSquashRXerrIP ) ;
2021-06-10 13:53:48 +00:00
// Side effect of reading IIR is lowering THRE_IP if most significant intr
2021-06-10 14:19:10 +00:00
assign setSquashTHRE_IP = ~ MEMRb & ( A = = 3 'b010 ) & ( intrID = = 3 'h1 ) ; // there's a 1-cycle delay on set squash so that THRE_IP doesn't change during the process of reading IIR (otherwise combinational loop)
2021-06-10 13:53:48 +00:00
assign resetSquashTHRE_IP = ~ THRE ;
assign squashTHRE_IP = prevSquashTHRE_IP & ~ resetSquashTHRE_IP ;
2022-07-06 13:26:14 +00:00
flopr # ( 1 ) squashTHRE_IPreg ( PCLK , ~ PRESETn , squashTHRE_IP | setSquashTHRE_IP , prevSquashTHRE_IP ) ;
2021-06-08 16:32:46 +00:00
2021-01-23 15:19:09 +00:00
///////////////////////////////////////////
// modem control logic
///////////////////////////////////////////
2023-06-15 17:20:29 +00:00
assign loop = MCR [ 4 ] ;
2021-01-23 15:19:09 +00:00
assign DTRb = ~ MCR [ 0 ] | loop ; // disable modem signals in loopback mode
assign RTSb = ~ MCR [ 1 ] | loop ;
assign OUT1b = ~ MCR [ 2 ] | loop ;
assign OUT2b = ~ MCR [ 3 ] | loop ;
assign DLAB = LCR [ 7 ] ;
2023-06-15 17:20:29 +00:00
assign evenparitysel = LCR [ 4 ] ;
assign fifoenabled = FCR [ 0 ] ;
2021-01-23 15:19:09 +00:00
assign fifodmamodesel = FCR [ 3 ] ;
always_comb
case ( FCR [ 7 : 6 ] )
2 'b00 : rxfifotriggerlevel = 1 ;
2 'b01 : rxfifotriggerlevel = 4 ;
2 'b10 : rxfifotriggerlevel = 8 ;
2 'b11 : rxfifotriggerlevel = 14 ;
endcase
endmodule
2021-01-27 11:40:26 +00:00
2021-12-15 16:25:08 +00:00
/* verilator lint_on UNOPTFLAT */