URL
https://opencores.org/ocsvn/uart6551/uart6551/trunk
Subversion Repositories uart6551
[/] [uart6551/] [trunk/] [trunk/] [rtl/] [uart6551.sv] - Rev 10
Compare with Previous | Blame | View Log
// ============================================================================// __// \\__/ o\ (C) 2005-2022 Robert Finch, Waterloo// \ __ / All rights reserved.// \/_// robfinch<remove>@finitron.ca// ||////// BSD 3-Clause License// Redistribution and use in source and binary forms, with or without// modification, are permitted provided that the following conditions are met://// 1. Redistributions of source code must retain the above copyright notice, this// list of conditions and the following disclaimer.//// 2. Redistributions in binary form must reproduce the above copyright notice,// this list of conditions and the following disclaimer in the documentation// and/or other materials provided with the distribution.//// 3. Neither the name of the copyright holder nor the names of its// contributors may be used to endorse or promote products derived from// this software without specific prior written permission.//// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.//// ============================================================================//`define UART_TRB 2'd0 // transmit/receive buffer`define UART_STAT 2'd1`define UART_CMD 2'd2`define UART_CTRL 2'd3module uart6551(rst_i, clk_i, cs_i, irq_o,cyc_i, stb_i, ack_o, we_i, sel_i, adr_i, dat_i, dat_o,cts_ni, rts_no, dsr_ni, dcd_ni, dtr_no, ri_ni,rxd_i, txd_o, data_present,rxDRQ_o, txDRQ_o,xclk_i, RxC_i);parameter pClkFreq = 100;parameter pCounterBits = 24;parameter pFifoSize = 1024;parameter pClkDiv = 24'd1302; // 9.6k baud, 200.000MHz clockparameter HIGH = 1'b1;parameter LOW = 1'b0;input rst_i;input clk_i; // eg 50.000MHzinput cs_i; // circuit select// WISHBONE -------------------------------input cyc_i; // bus cycle validinput stb_i;output ack_o;input we_i; // 1 = writeinput [3:0] sel_i;input [3:2] adr_i; // register addressinput [31:0] dat_i; // data input busoutput reg [31:0] dat_o; // data output bus//------------------------------------------output reg irq_o; // interrupt requestinput cts_ni; // clear to send - (flow control) active lowoutput reg rts_no; // request to send - (flow control) active lowinput dsr_ni; // data set ready - active lowinput dcd_ni; // data carrier detect - active lowoutput reg dtr_no; // data terminal ready - active lowinput ri_ni; // ring indicatorinput rxd_i; // serial data inoutput txd_o; // serial data outoutput data_present;output rxDRQ_o; // reciever DMA requestoutput txDRQ_o; // transmitter DMA requestinput xclk_i; // external clock sourceinput RxC_i; // external receiver clock sourcereg accessCD; // clock multiplier access flagreg llb; // local loopback modereg dmaEnable;// baud rate clock controlreg [4:0] baudRateSel;reg selCD; // Use clock multiplier registerreg [pCounterBits-1:0] c; // current countreg [pCounterBits-1:0] ckdiv; // baud rate clock dividerreg [pCounterBits-1:0] clkdiv; // clock multiplier registerreg [1:0] xclks; // synchronized external clockreg [1:0] RxCs; // synchronized external receiver clockreg baud16; // 16x baud rate clockwire baud16rx; // reciever clockreg xClkSrc; // uart baud clock is externalreg rxClkSrc; // receiver clock is external// frame format registersreg [3:0] wordLength;reg stopBit;reg [2:0] stopBits;reg [2:0] parityCtrl;wire [7:0] frameSize;reg txBreak; // transmit a breakwire rxFull;wire rxEmpty;wire txFull;wire txEmpty;reg hwfc; // hardware flow control enablewire [7:0] lineStatusReg;wire [7:0] modemStatusReg;wire [7:0] irqStatusReg;// interruptreg rxIe;reg txIe;reg modemStatusChangeIe;wire modemStatusChange;reg lineStatusChangeIe;wire lineStatusChange;reg rxToutIe; // receiver timeout interrupt enablereg [3:0] rxThres; // receiver threshold for interruptreg [3:0] txThres; // transmitter threshold for interruptreg rxTout; // receiver timeoutwire [9:0] rxCnt; // reciever counter valuereg [7:0] rxToutMax;reg [2:0] irqenc; // encoded irq causewire rxITrig; // receiver interrupt trigger levelwire txITrig; // transmitter interrupt trigger level// reciever errorswire parityErr; // reciever detected a parity errorwire frameErr; // receiver char framing errorwire overrun; // receiver over runwire rxBreak; // reciever detected a breakwire rxGErr; // global error: there is at least one error in the reciever fifo// modem controlsreg [1:0] ctsx; // cts_n samplingreg [1:0] dcdx;reg [1:0] dsrx;reg [1:0] rix;reg deltaCts;reg deltaDcd;reg deltaDsr;reg deltaRi;// fiforeg rxFifoClear;reg txFifoClear;reg txClear;reg fifoEnable;wire [3:0] rxQued;wire [3:0] txQued;// testwire txd1;assign data_present = ~rxEmpty;assign rxITrig = rxQued >= rxThres;assign txITrig = txQued <= txThres;wire rxDRQ1 = (fifoEnable ? rxITrig : ~rxEmpty);wire txDRQ1 = (fifoEnable ? txITrig : txEmpty);assign rxDRQ_o = dmaEnable & rxDRQ1;assign txDRQ_o = dmaEnable & txDRQ1;wire rxIRQ = rxIe & rxDRQ1;wire txIRQ = txIe & txDRQ1;reg [7:0] cmd0, cmd1, cmd2, cmd3;reg [7:0] ctrl0, ctrl1, ctrl2, ctrl3;always @(posedge clk_i)irq_o <=rxIRQ| txIRQ| (rxTout & rxToutIe)| (lineStatusChange & lineStatusChangeIe)| (modemStatusChange & modemStatusChangeIe);// Hold onto address and data an extra cycle.// The extra cycle updates or reads the serial transmit / receive.reg [31:0] dati;always @(posedge clk_i)dati <= dat_i;reg [3:2] adr_h;always @(posedge clk_i)adr_h <= adr_i;reg we;always @(posedge clk_i)we <= we_i;reg [3:0] sel;always @(posedge clk_i)sel <= sel_i;wire [7:0] rx_do;wire rdrx = ack_o && adr_h==`UART_TRB && ~we && !accessCD;wire txrx = ack_o && adr_h==`UART_TRB && !accessCD;wire cs = cs_i & cyc_i & stb_i;ack_gen #(.READ_STAGES(1),.WRITE_STAGES(0),.REGISTER_OUTPUT(1)) uag1(.clk_i(clk_i),.ce_i(1'b1),.rid_i('d0),.wid_i('d0),.i(cs & ~we_i),.we_i(cs & we_i),.o(ack_o),.rid_o(),.wid_o());uart6551Rx uart_rx0(.rst(rst_i),.clk(clk_i),.cyc(cyc_i),.cs(rdrx),.wr(we),.dout(rx_do),.ack(),.fifoEnable(fifoEnable),.fifoClear(rxFifoClear),.clearGErr(1'b0),.wordLength(wordLength),.parityCtrl(parityCtrl),.frameSize(frameSize),.stop_bits(stopBits),.baud16x_ce(baud16rx),.clear(1'b0),.rxd(llb ? txd1 : rxd_i),.full(),.empty(rxEmpty),.frameErr(frameErr),.overrun(overrun),.parityErr(parityErr),.break_o(rxBreak),.gerr(rxGErr),.qcnt(rxQued),.cnt(rxCnt));uart6551Tx uart_tx0(.rst(rst_i),.clk(clk_i),.cyc(cyc_i),.cs(txrx),.wr(we),.din(dati[7:0]),.ack(),.fifoEnable(fifoEnable),.fifoClear(txFifoClear),.txBreak(txBreak),.frameSize(frameSize), // 16 x 10 bits.wordLength(wordLength),// 8 bits.parityCtrl(parityCtrl),// no parity.baud16x_ce(baud16),.cts(ctsx[1]|~hwfc),.clear(txClear),.txd(txd1),.full(txFull),.empty(txEmpty),.qcnt(txQued));assign txd_o = llb ? 1'b1 : txd1;assign lineStatusReg = {rxGErr,1'b0,txFull,rxBreak,1'b0,1'b0,1'b0,1'b0};assign modemStatusChange = deltaDcd|deltaRi|deltaDsr|deltaCts; // modem status deltaassign modemStatusReg = {1'b0,~rix[1],1'b0,~ctsx[1],deltaDcd, deltaRi, deltaDsr, deltaCts};assign irqStatusReg = {irq_o,2'b00,irqenc,2'b00};// mux the reg outputsalways @(posedge clk_i)if (cs) begincase(adr_i)`UART_TRB: dat_o <= accessCD ? {8'h0,clkdiv} : {24'h0,rx_do}; // receiver holding register`UART_STAT: dat_o <= {irqStatusReg,modemStatusReg,lineStatusReg,irq_o,dsrx[1],dcdx[1],fifoEnable ? ~txFull : txEmpty,~rxEmpty,overrun,frameErr,parityErr};`UART_CMD: dat_o <= {cmd3,cmd2,cmd1,cmd0};`UART_CTRL: dat_o <= {ctrl3,ctrl2,ctrl1,ctrl0};endcaseendelsedat_o <= 'd0;// register updatesalways @(posedge clk_i)if (rst_i) beginrts_no <= HIGH;dtr_no <= HIGH;// interruptsrxIe <= 1'b0;txIe <= 1'b0;modemStatusChangeIe <= 1'b0;lineStatusChangeIe <= 1'b0;hwfc <= 1'b0;modemStatusChangeIe <= 1'b0;lineStatusChangeIe <= 1'b0;dmaEnable <= 1'b0;// clock controlbaudRateSel <= 5'h0;rxClkSrc <= 1'b0; // ** 6551 defaults to zero (external receiver clock)clkdiv <= pClkDiv;// frame formatwordLength <= 4'd8; // 8 bitsstopBit <= 1'b0; // 1 stop bitparityCtrl <= 3'd0; // no paritytxBreak <= 1'b0;// Fifo controltxFifoClear <= 1'b1;rxFifoClear <= 1'b1;txClear <= 1'b1;fifoEnable <= 1'b1;// Testllb <= 1'b0;selCD <= 1'b0;accessCD <= 1'b0;endelse begin//llb <= 1'b1;rxFifoClear <= 1'b0;txFifoClear <= 1'b0;txClear <= 1'b0;ctrl2[1] <= 1'b0;ctrl2[2] <= 1'b0;if (ack_o & we) begincase (adr_h) // synopsys full_case parallel_case`UART_TRB:if (accessCD) beginclkdiv <= dati;accessCD <= 1'b0;ctrl3[7] <= 1'b0;end// Writing to the status register does a software reset of some bits.`UART_STAT:begindtr_no <= HIGH;rxIe <= 1'b0;rts_no <= HIGH;txIe <= 1'b0;txBreak <= 1'b0;llb <= 1'b0;end`UART_CMD:beginif (sel[0]) begincmd0 <= dati[7:0];dtr_no <= ~dati[0];rxIe <= ~dati[1];case(dati[3:2])2'd0: begin rts_no <= 1'b1; txIe <= 1'b0; txBreak <= 1'b0; end2'd1: begin rts_no <= 1'b0; txIe <= 1'b1; txBreak <= 1'b0; end2'd2: begin rts_no <= 1'b0; txIe <= 1'b0; txBreak <= 1'b0; end2'd3: begin rts_no <= 1'b0; txIe <= 1'b0; txBreak <= 1'b1; endendcasellb <= dati[4];parityCtrl <= dati[7:5]; //000=none,001=odd,011=even,101=force 1,111 = force 0endif (sel[1]) begincmd1 <= dati[15:8];lineStatusChangeIe <= dati[8];modemStatusChangeIe <= dati[9];rxToutIe <= dati[10];endif (sel[2])cmd2 <= dati[23:16];if (sel[3])cmd3 <= dati[31:24];end`UART_CTRL:beginif (sel[0]) beginctrl0 <= dati[7:0];baudRateSel[3:0] <= dati[3:0];rxClkSrc <= dati[4]; // 1 = baud rate generator, 0 = external//11=5,10=6,01=7,00=8case(dati[6:5])2'd0: wordLength <= 6'd8;2'd1: wordLength <= 6'd7;2'd2: wordLength <= 6'd6;2'd3: wordLength <= 6'd5;endcasestopBit <= dati[7]; //0=1,1=1.5 or 2end// Extended word length, values beyond 11 not supported.if (sel[1]) beginctrl1 <= dati[15:8];endif (sel[2]) beginctrl2 <= dati[23:16];fifoEnable <= dati[16];rxFifoClear <= dati[17];txFifoClear <= dati[18];case (dati[21:20])2'd0: txThres <= 4'd1; // one-byte2'd1: txThres <= pFifoSize / 4; // one-quarter full2'd2: txThres <= pFifoSize / 2; // one-half full2'd3: txThres <= pFifoSize * 3 / 4; // three-quarters fullendcasecase (dati[23:22])2'd0: rxThres <= 4'd1; // one-byte2'd1: rxThres <= pFifoSize / 4; // one-quarter full2'd2: rxThres <= pFifoSize / 2; // one-half full2'd3: rxThres <= pFifoSize * 3 / 4; // three quarters fullendcaseendif (sel[3]) beginctrl3 <= dati[31:24];hwfc <= dati[24];dmaEnable <= dati[26];baudRateSel[4] <= dati[27];txClear <= dati[29];selCD <= dati[30];accessCD <= dati[31];endenddefault:;endcaseendend// ----------------------------------------------------------------------------// Baud rate control.// ----------------------------------------------------------------------------always @(posedge clk_i)xClkSrc <= baudRateSel==5'd0;wire [pCounterBits-1:0] bclkdiv;uart6551BaudLUT #(.pClkFreq(pClkFreq), .pCounterBits(pCounterBits)) ublt1 (.a(baudRateSel), .o(bclkdiv));reg [pCounterBits-1:0] clkdiv2;always @(posedge clk_i)clkdiv2 <= selCD ? clkdiv : bclkdiv;always @(posedge clk_i)if (rst_i)c <= 24'd1;else beginc <= c + 2'd1;if (c >= clkdiv2)c <= 2'd1;end// for detecting an edge on the baud clockwire ibaud16 = c == 2'd1;// Detect an edge on the external clockwire xclkEdge;edge_det ed1(.rst(rst_i), .clk(clk_i), .ce(1'b1), .i(xclks[1]), .pe(xclkEdge), .ne() );// Detect an edge on the external clockwire rxClkEdge;edge_det ed2(.rst(rst_i), .clk(clk_i), .ce(1'b1), .i(RxCs[1]), .pe(rxClkEdge), .ne() );always_combif (xClkSrc) // 16x external clock (xclk)baud16 <= xclkEdge;elsebaud16 <= ibaud16;assign baud16rx = rxClkSrc ? baud16 : rxClkEdge;//------------------------------------------------------------// external signal synchronization//------------------------------------------------------------// External receiver clockalways @(posedge clk_i)RxCs <= {RxCs[1:0],RxC_i};// External baud clockalways @(posedge clk_i)xclks <= {xclks[1:0],xclk_i};always @(posedge clk_i)ctsx <= {ctsx[0],llb?~rts_no:~cts_ni};always @(posedge clk_i)dcdx <= {dcdx[0],~dcd_ni};always @(posedge clk_i)dsrx <= {dsrx[0],llb?~dtr_no:~dsr_ni};always @(posedge clk_i)rix <= {rix[0],~ri_ni};//------------------------------------------------------------// state change detectors//------------------------------------------------------------wire ne_stat;edge_det ued3 (.rst(rst_i),.clk(clk_i),.ce(1'b1),.i(ack_o && adr_i==`UART_STAT && ~we_i && sel_i[2]),.pe(),.ne(ne_stat),.ee());// detect a change on the dsr signalalways @(posedge clk_i)if (rst_i)deltaDsr <= 1'b0;else beginif (ne_stat)deltaDsr <= 0;else if (~deltaDsr)deltaDsr <= dsrx[1] ^ dsrx[0];end// detect a change on the dcd signalalways @(posedge clk_i)if (rst_i)deltaDcd <= 1'b0;else beginif (ne_stat)deltaDcd <= 0;else if (~deltaDcd)deltaDcd <= dcdx[1] ^ dcdx[0];end// detect a change on the cts signalalways @(posedge clk_i)if (rst_i)deltaCts <= 1'b0;else beginif (ne_stat)deltaCts <= 0;else if (~deltaCts)deltaCts <= ctsx[1] ^ ctsx[0];end// detect a change on the ri signalalways @(posedge clk_i)if (rst_i)deltaRi <= 1'b0;else beginif (ne_stat)deltaRi <= 0;else if (~deltaRi)deltaRi <= rix[1] ^ rix[0];end// detect a change in line statusreg [7:0] pLineStatusReg;always @(posedge clk_i)pLineStatusReg <= lineStatusReg;assign lineStatusChange = pLineStatusReg != lineStatusReg;//-----------------------------------------------------// compute recieve timeoutalways @(wordLength)rxToutMax <= (wordLength << 2) + 6'd12;always @(posedge clk_i)if (rst_i)rxTout <= 1'b0;else begin// read of receiver clears timeout counterif (rdrx)rxTout <= 1'b0;// Don't time out if the fifo is emptyelse if (rxCnt[9:4]==rxToutMax && ~rxEmpty)rxTout <= 1'b1;end//-----------------------------------------------------// compute the 2x number of stop bitsalways @*if (stopBit==1'b0) // one stop bitstopBits <= 3'd2;else if (wordLength==6'd8 && parityCtrl != 3'd0)stopBits <= 3'd2;else if (wordLength==6'd5 && parityCtrl == 3'd0) // 5 bits - 1 1/2 stop bitstopBits <= 3'd3;elsestopBits <= 3'd4; // two stop bits// compute frame size// frame size is one lessassign frameSize = {wordLength + 4'd1 + stopBits[2:1] + parityCtrl[0], stopBits[0],3'b0} - 1;//-----------------------------------------------------// encode IRQ mailboxalways @(rxDRQ_o or rxTout or txDRQ_o or lineStatusChange or modemStatusChange)irqenc <=lineStatusChange ? 3'd0 :~rxDRQ_o ? 3'd1 :rxTout ? 3'd2 :~txDRQ_o ? 3'd3 :modemStatusChange ? 3'd4 :3'd0;endmodule
