parameterized register names in peripherals

This commit is contained in:
David Harris 2024-04-21 07:43:01 -07:00
parent 00a1c0fc57
commit 0419b5484a
6 changed files with 271 additions and 210 deletions

View File

@ -42,6 +42,11 @@ module clint_apb import cvw::*; #(parameter cvw_t P) (
output logic MTimerInt, MSwInt output logic MTimerInt, MSwInt
); );
// register map
localparam CLINT_MSIP = 16'h0000;
localparam CLINT_MTIMECMP = 16'h4000;
localparam CLINT_MTIME = 16'hBFF8;
logic MSIP; logic MSIP;
logic [15:0] entry; logic [15:0] entry;
logic memwrite; logic memwrite;
@ -65,19 +70,19 @@ module clint_apb import cvw::*; #(parameter cvw_t P) (
if (P.XLEN==64) begin:clint // 64-bit if (P.XLEN==64) begin:clint // 64-bit
always_ff @(posedge PCLK) begin always_ff @(posedge PCLK) begin
case(entry) case(entry)
16'h0000: PRDATA <= {63'b0, MSIP}; CLINT_MSIP: PRDATA <= {63'b0, MSIP};
16'h4000: PRDATA <= MTIMECMP; CLINT_MTIMECMP: PRDATA <= MTIMECMP;
16'hBFF8: PRDATA <= MTIME; CLINT_MTIME: PRDATA <= MTIME;
default: PRDATA <= 0; default: PRDATA <= '0;
endcase endcase
end end
always_ff @(posedge PCLK or negedge PRESETn) always_ff @(posedge PCLK or negedge PRESETn)
if (~PRESETn) begin if (~PRESETn) begin
MSIP <= 0; MSIP <= 1'b0;
MTIMECMP <= 64'hFFFFFFFFFFFFFFFF; // Spec says MTIMECMP is not reset, but we reset to maximum value to prevent spurious timer interrupts MTIMECMP <= 64'hFFFFFFFFFFFFFFFF; // Spec says MTIMECMP is not reset, but we reset to maximum value to prevent spurious timer interrupts
end else if (memwrite) begin end else if (memwrite) begin
if (entry == 16'h0000) MSIP <= PWDATA[0]; if (entry == CLINT_MSIP) MSIP <= PWDATA[0];
if (entry == 16'h4000) begin if (entry == CLINT_MTIMECMP) begin
for(i=0;i<P.XLEN/8;i++) for(i=0;i<P.XLEN/8;i++)
if(PSTRB[i]) if(PSTRB[i])
MTIMECMP[i*8 +: 8] <= PWDATA[i*8 +: 8]; // ***dh: this notation isn't in book yet - maybe from Ross MTIMECMP[i*8 +: 8] <= PWDATA[i*8 +: 8]; // ***dh: this notation isn't in book yet - maybe from Ross
@ -89,7 +94,7 @@ module clint_apb import cvw::*; #(parameter cvw_t P) (
always_ff @(posedge PCLK or negedge PRESETn) always_ff @(posedge PCLK or negedge PRESETn)
if (~PRESETn) begin if (~PRESETn) begin
MTIME <= 0; MTIME <= '0;
end else if (memwrite & entry == 16'hBFF8) begin end else if (memwrite & entry == 16'hBFF8) begin
// MTIME Counter. Eventually change this to run off separate clock. Synchronization then needed // MTIME Counter. Eventually change this to run off separate clock. Synchronization then needed
for(j=0;j<P.XLEN/8;j++) for(j=0;j<P.XLEN/8;j++)
@ -104,13 +109,13 @@ module clint_apb import cvw::*; #(parameter cvw_t P) (
16'h4004: PRDATA <= MTIMECMP[63:32]; 16'h4004: PRDATA <= MTIMECMP[63:32];
16'hBFF8: PRDATA <= MTIME[31:0]; 16'hBFF8: PRDATA <= MTIME[31:0];
16'hBFFC: PRDATA <= MTIME[63:32]; 16'hBFFC: PRDATA <= MTIME[63:32];
default: PRDATA <= 0; default: PRDATA <= '0;
endcase endcase
end end
always_ff @(posedge PCLK or negedge PRESETn) always_ff @(posedge PCLK or negedge PRESETn)
if (~PRESETn) begin if (~PRESETn) begin
MSIP <= 0; MSIP <= 1'b0;
MTIMECMP <= 0; MTIMECMP <= '0;
// MTIMECMP is not reset ***? // MTIMECMP is not reset ***?
end else if (memwrite) begin end else if (memwrite) begin
if (entry == 16'h0000) MSIP <= PWDATA[0]; if (entry == 16'h0000) MSIP <= PWDATA[0];
@ -129,7 +134,7 @@ module clint_apb import cvw::*; #(parameter cvw_t P) (
// timereg tr(PCLK, PRESETn, TIMECLK, memwrite & (entry==16'hBFF8), memwrite & (entry == 16'hBFFC), PWDATA, MTIME, done); // timereg tr(PCLK, PRESETn, TIMECLK, memwrite & (entry==16'hBFF8), memwrite & (entry == 16'hBFFC), PWDATA, MTIME, done);
always_ff @(posedge PCLK or negedge PRESETn) always_ff @(posedge PCLK or negedge PRESETn)
if (~PRESETn) begin if (~PRESETn) begin
MTIME <= 0; MTIME <= '0;
// MTIMECMP is not reset // MTIMECMP is not reset
end else if (memwrite & (entry == 16'hBFF8)) begin end else if (memwrite & (entry == 16'hBFF8)) begin
for(i=0;i<P.XLEN/8;i++) for(i=0;i<P.XLEN/8;i++)
@ -159,12 +164,12 @@ module timeregsync import cvw::*; #(parameter cvw_t P) (
if (P.XLEN==64) if (P.XLEN==64)
always_ff @(posedge clk or negedge resetn) always_ff @(posedge clk or negedge resetn)
if (~resetn) q <= 0; if (~resetn) q <= '0;
else if (we0) q <= wd; else if (we0) q <= wd;
else q <= q + 1; else q <= q + 1;
else else
always_ff @(posedge clk or negedge resetn) always_ff @(posedge clk or negedge resetn)
if (~resetn) q <= 0; if (~resetn) q <= '0;
else if (we0) q[31:0] <= wd; else if (we0) q[31:0] <= wd;
else if (we1) q[63:32] <= wd; else if (we1) q[63:32] <= wd;
else q <= q + 1; else q <= q + 1;

View File

@ -45,6 +45,23 @@ module gpio_apb import cvw::*; #(parameter cvw_t P) (
output logic GPIOIntr output logic GPIOIntr
); );
// register map
localparam GPIO_INPUT_VAL = 8'h00;
localparam GPIO_INPUT_EN = 8'h04;
localparam GPIO_OUTPUT_EN = 8'h08;
localparam GPIO_OUTPUT_VAL = 8'h0C;
localparam GPIO_RISE_IE = 8'h18;
localparam GPIO_RISE_IP = 8'h1C;
localparam GPIO_FALL_IE = 8'h20;
localparam GPIO_FALL_IP = 8'h24;
localparam GPIO_HIGH_IE = 8'h28;
localparam GPIO_HIGH_IP = 8'h2C;
localparam GPIO_LOW_IE = 8'h30;
localparam GPIO_LOW_IP = 8'h34;
localparam GPIO_IOF_EN = 8'h38;
localparam GPIO_IOF_SEL = 8'h3C;
localparam GPIO_OUT_XOR = 8'h40;
logic [31:0] input0d, input1d, input2d, input3d; logic [31:0] input0d, input1d, input2d, input3d;
logic [31:0] input_val, input_en, output_en, output_val; logic [31:0] input_val, input_en, output_en, output_val;
logic [31:0] rise_ie, rise_ip, fall_ie, fall_ip, high_ie, high_ip, low_ie, low_ip; logic [31:0] rise_ie, rise_ip, fall_ie, fall_ip, high_ie, high_ip, low_ie, low_ip;
@ -67,66 +84,66 @@ module gpio_apb import cvw::*; #(parameter cvw_t P) (
// register access // register access
always_ff @(posedge PCLK, negedge PRESETn) always_ff @(posedge PCLK, negedge PRESETn)
if (~PRESETn) begin // asynch reset if (~PRESETn) begin // asynch reset
input_en <= 0; input_en <= '0;
output_en <= 0; output_en <= '0;
// *** synch reset not yet implemented [DH: can we delete this comment? Check if a sync reset is required] // *** synch reset not yet implemented [DH: can we delete this comment? Check if a sync reset is required]
output_val <= 0; output_val <= '0;
rise_ie <= 0; rise_ie <= '0;
rise_ip <= 0; rise_ip <= '0;
fall_ie <= 0; fall_ie <= '0;
fall_ip <= 0; fall_ip <= '0;
high_ie <= 0; high_ie <= '0;
high_ip <= 0; high_ip <= '0;
low_ie <= 0; low_ie <= '0;
low_ip <= 0; low_ip <= '0;
iof_en <= 0; iof_en <= '0;
iof_sel <= 0; iof_sel <= '0;
out_xor <= 0; out_xor <= '0;
end else begin // writes end else begin // writes
// According to FE310 spec: Once the interrupt is pending, it will remain set until a 1 is written to the *_ip register at that bit. // According to FE310 spec: Once the interrupt is pending, it will remain set until a 1 is written to the *_ip register at that bit.
/* verilator lint_off CASEINCOMPLETE */ /* verilator lint_off CASEINCOMPLETE */
if (memwrite) if (memwrite)
case(entry) case(entry)
8'h04: input_en <= Din; GPIO_INPUT_EN: input_en <= Din;
8'h08: output_en <= Din; GPIO_OUTPUT_EN: output_en <= Din;
8'h0C: output_val <= Din; GPIO_OUTPUT_VAL: output_val <= Din;
8'h18: rise_ie <= Din; GPIO_RISE_IE: rise_ie <= Din;
8'h20: fall_ie <= Din; GPIO_FALL_IE: fall_ie <= Din;
8'h28: high_ie <= Din; GPIO_HIGH_IE: high_ie <= Din;
8'h30: low_ie <= Din; GPIO_LOW_IE: low_ie <= Din;
8'h38: iof_en <= Din; GPIO_IOF_EN: iof_en <= Din;
8'h3C: iof_sel <= Din; GPIO_IOF_SEL: iof_sel <= Din;
8'h40: out_xor <= Din; GPIO_OUT_XOR: out_xor <= Din;
endcase endcase
/* verilator lint_on CASEINCOMPLETE */ /* verilator lint_on CASEINCOMPLETE */
// interrupts can be cleared by writing corresponding bits to a register // interrupts can be cleared by writing corresponding bits to a register
if (memwrite & entry == 8'h1C) rise_ip <= rise_ip & ~Din; if (memwrite & entry == GPIO_RISE_IP) rise_ip <= rise_ip & ~Din;
else rise_ip <= rise_ip | (input2d & ~input3d); else rise_ip <= rise_ip | (input2d & ~input3d);
if (memwrite & (entry == 8'h24)) fall_ip <= fall_ip & ~Din; if (memwrite & (entry == GPIO_FALL_IP)) fall_ip <= fall_ip & ~Din;
else fall_ip <= fall_ip | (~input2d & input3d); else fall_ip <= fall_ip | (~input2d & input3d);
if (memwrite & (entry == 8'h2C)) high_ip <= high_ip & ~Din; if (memwrite & (entry == GPIO_HIGH_IP)) high_ip <= high_ip & ~Din;
else high_ip <= high_ip | input3d; else high_ip <= high_ip | input3d;
if (memwrite & (entry == 8'h34)) low_ip <= low_ip & ~Din; if (memwrite & (entry == GPIO_LOW_IP)) low_ip <= low_ip & ~Din;
else low_ip <= low_ip | ~input3d; else low_ip <= low_ip | ~input3d;
case(entry) // flop to sample inputs case(entry) // flop to sample inputs
8'h00: Dout <= input_val; GPIO_INPUT_VAL: Dout <= input_val;
8'h04: Dout <= input_en; GPIO_INPUT_EN: Dout <= input_en;
8'h08: Dout <= output_en; GPIO_OUTPUT_EN: Dout <= output_en;
8'h0C: Dout <= output_val; GPIO_OUTPUT_VAL: Dout <= output_val;
8'h18: Dout <= rise_ie; GPIO_RISE_IE: Dout <= rise_ie;
8'h1C: Dout <= rise_ip; GPIO_RISE_IP: Dout <= rise_ip;
8'h20: Dout <= fall_ie; GPIO_FALL_IE: Dout <= fall_ie;
8'h24: Dout <= fall_ip; GPIO_FALL_IP: Dout <= fall_ip;
8'h28: Dout <= high_ie; GPIO_HIGH_IE: Dout <= high_ie;
8'h2C: Dout <= high_ip; GPIO_HIGH_IP: Dout <= high_ip;
8'h30: Dout <= low_ie; GPIO_LOW_IE: Dout <= low_ie;
8'h34: Dout <= low_ip; GPIO_LOW_IP: Dout <= low_ip;
8'h38: Dout <= iof_en; GPIO_IOF_EN: Dout <= iof_en;
8'h3C: Dout <= iof_sel; GPIO_IOF_SEL: Dout <= iof_sel;
8'h40: Dout <= out_xor; GPIO_OUT_XOR: Dout <= out_xor;
default: Dout <= 0; default: Dout <= '0;
endcase endcase
end end

View File

@ -54,6 +54,19 @@ module plic_apb import cvw::*; #(parameter cvw_t P) (
output logic MExtInt, SExtInt output logic MExtInt, SExtInt
); );
// register map
localparam PLIC_INTPRIORITY0 = 24'h000000;
localparam PLIC_INTPENDING0 = 24'h001000;
localparam PLIC_INTPENDING1 = 24'h001004;
localparam PLIC_INTEN00 = 24'h002000;
localparam PLIC_INTEN01 = 24'h002004;
localparam PLIC_INTEN10 = 24'h002080;
localparam PLIC_INTEN11 = 24'h002084;
localparam PLIC_THRESHOLD0 = 24'h200000;
localparam PLIC_CLAIMCOMPLETE0 = 24'h200004;
localparam PLIC_THRESHOLD1 = 24'h201000;
localparam PLIC_CLAIMCOMPLETE1 = 24'h201004;
logic memwrite, memread; logic memwrite, memread;
logic [23:0] entry; logic [23:0] entry;
logic [31:0] Din, Dout; logic [31:0] Din, Dout;
@ -91,7 +104,7 @@ module plic_apb import cvw::*; #(parameter cvw_t P) (
assign memread = ~PWRITE & PSEL; // read at start of access phase. PENABLE hasn't set up before this assign memread = ~PWRITE & PSEL; // read at start of access phase. PENABLE hasn't set up before this
assign PREADY = 1'b1; // PLIC never takes >1 cycle to respond assign PREADY = 1'b1; // PLIC never takes >1 cycle to respond
assign entry = {PADDR[23:2],2'b0}; assign entry = {PADDR[23:2],2'b0};
assign One[P.PLIC_NUM_SRC-1:1] = 0; assign One[0] = 1'b1; // Vivado does not like this as a single assignment. assign One[P.PLIC_NUM_SRC-1:1] = '0; assign One[0] = 1'b1; // Vivado does not like this as a single assignment.
// account for subword read/write circuitry // account for subword read/write circuitry
// -- Note PLIC registers are 32 bits no matter what; access them with LW SW. // -- Note PLIC registers are 32 bits no matter what; access them with LW SW.
@ -107,48 +120,49 @@ module plic_apb import cvw::*; #(parameter cvw_t P) (
always_ff @(posedge PCLK) begin always_ff @(posedge PCLK) begin
// resetting // resetting
if (~PRESETn) begin if (~PRESETn) begin
intPriority <= 0; intPriority <= '0;
intEn <= 0; intEn <= '0;
intThreshold <= 0; intThreshold <= '0;
intInProgress <= 0; intInProgress <= '0;
// writing // writing
end else begin end else begin
if (memwrite) if (memwrite)
casez(entry) casez(entry)
24'h0000??: intPriority[entry[7:2]] <= Din[2:0]; 24'h0000??: intPriority[entry[7:2]] <= Din[2:0];
24'h002000: intEn[0][PLIC_NUM_SRC_MIN_32:1] <= Din[PLIC_NUM_SRC_MIN_32:1]; PLIC_INTEN00: intEn[0][PLIC_NUM_SRC_MIN_32:1] <= Din[PLIC_NUM_SRC_MIN_32:1];
24'h002080: intEn[1][PLIC_NUM_SRC_MIN_32:1] <= Din[PLIC_NUM_SRC_MIN_32:1]; PLIC_INTEN10: intEn[1][PLIC_NUM_SRC_MIN_32:1] <= Din[PLIC_NUM_SRC_MIN_32:1];
24'h002004: if (P.PLIC_NUM_SRC >= 32) intEn[0][PLIC_SRC_TOP:PLIC_SRC_BOT] <= Din[PLIC_SRC_DINTOP:0]; PLIC_INTEN01: if (P.PLIC_NUM_SRC >= 32) intEn[0][PLIC_SRC_TOP:PLIC_SRC_BOT] <= Din[PLIC_SRC_DINTOP:0];
24'h002084: if (P.PLIC_NUM_SRC >= 32) intEn[1][PLIC_SRC_TOP:PLIC_SRC_BOT] <= Din[PLIC_SRC_DINTOP:0]; PLIC_INTEN11: if (P.PLIC_NUM_SRC >= 32) intEn[1][PLIC_SRC_TOP:PLIC_SRC_BOT] <= Din[PLIC_SRC_DINTOP:0];
24'h200000: intThreshold[0] <= Din[2:0]; PLIC_THRESHOLD0: intThreshold[0] <= Din[2:0];
24'h200004: intInProgress <= intInProgress & ~(One << (Din[5:0]-1)); // lower "InProgress" to signify completion PLIC_CLAIMCOMPLETE0: intInProgress <= intInProgress & ~(One << (Din[5:0]-1)); // lower "InProgress" to signify completion
24'h201000: intThreshold[1] <= Din[2:0]; PLIC_THRESHOLD1: intThreshold[1] <= Din[2:0];
24'h201004: intInProgress <= intInProgress & ~(One << (Din[5:0]-1)); // lower "InProgress" to signify completion PLIC_CLAIMCOMPLETE1: intInProgress <= intInProgress & ~(One << (Din[5:0]-1)); // lower "InProgress" to signify completion
endcase endcase
// Read synchronously because a read can have side effect of changing intInProgress // Read synchronously because a read can have side effect of changing intInProgress
if (memread) begin if (memread) begin
casez(entry) casez(entry)
24'h000000: Dout <= 32'b0; // there is no intPriority[0] PLIC_INTPRIORITY0: Dout <= 32'b0; // there is no intPriority[0]
24'h0000??: Dout <= {29'b0,intPriority[entry[7:2]]}; 24'h0000??: Dout <= {29'b0,intPriority[entry[7:2]]};
24'h001000: Dout <= {{(31-PLIC_NUM_SRC_MIN_32){1'b0}},intPending[PLIC_NUM_SRC_MIN_32:1],1'b0}; PLIC_INTPENDING0: Dout <= {{(31-PLIC_NUM_SRC_MIN_32){1'b0}},intPending[PLIC_NUM_SRC_MIN_32:1],1'b0};
24'h002000: Dout <= {{(31-PLIC_NUM_SRC_MIN_32){1'b0}},intEn[0][PLIC_NUM_SRC_MIN_32:1],1'b0}; PLIC_INTEN00: Dout <= {{(31-PLIC_NUM_SRC_MIN_32){1'b0}},intEn[0][PLIC_NUM_SRC_MIN_32:1],1'b0};
24'h001004: if (P.PLIC_NUM_SRC >= 32) Dout <= {{(PLIC_SRC_EXT){1'b0}},intPending[PLIC_SRC_TOP:PLIC_SRC_BOT]}; PLIC_INTPENDING1: if (P.PLIC_NUM_SRC >= 32) Dout <= {{(PLIC_SRC_EXT){1'b0}},intPending[PLIC_SRC_TOP:PLIC_SRC_BOT]};
24'h002004: if (P.PLIC_NUM_SRC >= 32) Dout <= {{(PLIC_SRC_EXT){1'b0}},intEn[0][PLIC_SRC_TOP:PLIC_SRC_BOT]}; PLIC_INTEN01: if (P.PLIC_NUM_SRC >= 32) Dout <= {{(PLIC_SRC_EXT){1'b0}},intEn[0][PLIC_SRC_TOP:PLIC_SRC_BOT]};
24'h002080: Dout <= {{(31-PLIC_NUM_SRC_MIN_32){1'b0}},intEn[1][PLIC_NUM_SRC_MIN_32:1],1'b0}; PLIC_INTEN10: Dout <= {{(31-PLIC_NUM_SRC_MIN_32){1'b0}},intEn[1][PLIC_NUM_SRC_MIN_32:1],1'b0};
24'h002084: if (P.PLIC_NUM_SRC >= 32) Dout <= {{(PLIC_SRC_EXT){1'b0}},intEn[1][PLIC_SRC_TOP:PLIC_SRC_BOT]}; PLIC_INTEN11: if (P.PLIC_NUM_SRC >= 32) Dout <= {{(PLIC_SRC_EXT){1'b0}},intEn[1][PLIC_SRC_TOP:PLIC_SRC_BOT]};
24'h200000: Dout <= {29'b0,intThreshold[0]}; PLIC_THRESHOLD0: Dout <= {29'b0,intThreshold[0]};
24'h200004: begin PLIC_CLAIMCOMPLETE0: begin
Dout <= {26'b0,intClaim[0]}; Dout <= {26'b0,intClaim[0]};
intInProgress <= intInProgress | (One << (intClaim[0]-1)); // claimed requests are currently in progress of being serviced until they are completed intInProgress <= intInProgress | (One << (intClaim[0]-1)); // claimed requests are currently in progress of being serviced until they are completed
end end
24'h201000: Dout <= {29'b0,intThreshold[1]}; PLIC_THRESHOLD1: Dout <= {29'b0,intThreshold[1]};
24'h201004: begin PLIC_CLAIMCOMPLETE1: begin
Dout <= {26'b0,intClaim[1]}; Dout <= {26'b0,intClaim[1]};
intInProgress <= intInProgress | (One << (intClaim[1]-1)); // claimed requests are currently in progress of being serviced until they are completed intInProgress <= intInProgress | (One << (intClaim[1]-1)); // claimed requests are currently in progress of being serviced until they are completed
end end
default: Dout <= 32'h0; // invalid access default: Dout <= 32'h0; // invalid access
endcase endcase
end else Dout <= 32'h0; end else Dout <= 32'h0;
end end
end end

View File

@ -64,7 +64,7 @@ module ram_ahb import cvw::*; #(parameter cvw_t P,
assign nextHREADYRam = (~(memwriteD & memread)) & ~DelayReady; assign nextHREADYRam = (~(memwriteD & memread)) & ~DelayReady;
flopr #(1) readyreg(HCLK, ~HRESETn, nextHREADYRam, HREADYRam); flopr #(1) readyreg(HCLK, ~HRESETn, nextHREADYRam, HREADYRam);
assign HRESPRam = 0; // OK assign HRESPRam = 1'b0; // OK
// On writes or during a wait state, use address delayed by one cycle to sync RamAddr with HWDATA or hold stalled address // On writes or during a wait state, use address delayed by one cycle to sync RamAddr with HWDATA or hold stalled address
mux2 #(P.PA_BITS) adrmux(HADDR, HADDRD, memwriteD | ~HREADY, RamAddr); mux2 #(P.PA_BITS) adrmux(HADDR, HADDRD, memwriteD | ~HREADY, RamAddr);
@ -104,7 +104,7 @@ module ram_ahb import cvw::*; #(parameter cvw_t P,
assign DelayReady = NextState == DELAY; assign DelayReady = NextState == DELAY;
assign CntRst = NextState == READY; assign CntRst = NextState == READY;
end else begin end else begin
assign DelayReady = 0; assign DelayReady = 1'b0;
end end
endmodule endmodule

View File

@ -45,6 +45,22 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
output logic SPIIntr output logic SPIIntr
); );
// register map
localparam SPI_SCKDIV = 8'h00;
localparam SPI_SCKMODE = 8'h04;
localparam SPI_CSID = 8'h10;
localparam SPI_CSDEF = 8'h14;
localparam SPI_CSMODE = 8'h18;
localparam SPI_DELAY0 = 8'h28;
localparam SPI_DELAY1 = 8'h2C;
localparam SPI_FMT = 8'h40;
localparam SPI_TXDATA = 8'h48;
localparam SPI_RXDATA = 8'h4C;
localparam SPI_TXMARK = 8'h50;
localparam SPI_RXMARK = 8'h54;
localparam SPI_IE = 8'h70;
localparam SPI_IP = 8'h74;
// SPI control registers. Refer to SiFive FU540-C000 manual // SPI control registers. Refer to SiFive FU540-C000 manual
logic [11:0] SckDiv; logic [11:0] SckDiv;
logic [1:0] SckMode; logic [1:0] SckMode;
@ -61,7 +77,7 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
// Bus interface signals // Bus interface signals
logic [7:0] Entry; logic [7:0] Entry;
logic Memwrite; logic Memwrite;
logic [31:0] Din, Dout; logic [31:0] Din, Dout;
logic TransmitInactive; // High when there is no transmission, used as hardware interlock signal logic TransmitInactive; // High when there is no transmission, used as hardware interlock signal
// FIFO FSM signals // FIFO FSM signals
@ -130,8 +146,8 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
// -- Note SPI registers are 32 bits no matter what; access them with LW SW. // -- Note SPI registers are 32 bits no matter what; access them with LW SW.
assign Din = PWDATA[31:0]; assign Din = PWDATA[31:0];
if (P.XLEN == 64) assign PRDATA = {Dout, Dout}; if (P.XLEN == 64) assign PRDATA = { Dout, Dout};
else assign PRDATA = Dout; else assign PRDATA = Dout;
// Register access // Register access
always_ff@(posedge PCLK, negedge PRESETn) always_ff@(posedge PCLK, negedge PRESETn)
@ -140,7 +156,7 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
SckMode <= 2'b0; SckMode <= 2'b0;
ChipSelectID <= 2'b0; ChipSelectID <= 2'b0;
ChipSelectDef <= 4'b1111; ChipSelectDef <= 4'b1111;
ChipSelectMode <= 0; ChipSelectMode <= 2'b0;
Delay0 <= {8'b1,8'b1}; Delay0 <= {8'b1,8'b1};
Delay1 <= {8'b0,8'b1}; Delay1 <= {8'b0,8'b1};
Format <= {5'b10000}; Format <= {5'b10000};
@ -155,18 +171,18 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
/* verilator lint_off CASEINCOMPLETE */ /* verilator lint_off CASEINCOMPLETE */
if (Memwrite & TransmitInactive) if (Memwrite & TransmitInactive)
case(Entry) // flop to sample inputs case(Entry) // flop to sample inputs
8'h00: SckDiv <= Din[11:0]; SPI_SCKDIV: SckDiv <= Din[11:0];
8'h04: SckMode <= Din[1:0]; SPI_SCKMODE: SckMode <= Din[1:0];
8'h10: ChipSelectID <= Din[1:0]; SPI_CSID: ChipSelectID <= Din[1:0];
8'h14: ChipSelectDef <= Din[3:0]; SPI_CSDEF: ChipSelectDef <= Din[3:0];
8'h18: ChipSelectMode <= Din[1:0]; SPI_CSMODE: ChipSelectMode <= Din[1:0];
8'h28: Delay0 <= {Din[23:16], Din[7:0]}; SPI_DELAY0: Delay0 <= {Din[23:16], Din[7:0]};
8'h2C: Delay1 <= {Din[23:16], Din[7:0]}; SPI_DELAY1: Delay1 <= {Din[23:16], Din[7:0]};
8'h40: Format <= {Din[19:16], Din[2]}; SPI_FMT: Format <= {Din[19:16], Din[2]};
8'h48: if (~TransmitFIFOWriteFull) TransmitData[7:0] <= Din[7:0]; SPI_TXDATA: if (~TransmitFIFOWriteFull) TransmitData[7:0] <= Din[7:0];
8'h50: TransmitWatermark <= Din[2:0]; SPI_TXMARK: TransmitWatermark <= Din[2:0];
8'h54: ReceiveWatermark <= Din[2:0]; SPI_RXMARK: ReceiveWatermark <= Din[2:0];
8'h70: InterruptEnable <= Din[1:0]; SPI_IE: InterruptEnable <= Din[1:0];
endcase endcase
/* verilator lint_off CASEINCOMPLETE */ /* verilator lint_off CASEINCOMPLETE */
@ -176,21 +192,21 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
InterruptPending[1] <= RecieveWriteMark; InterruptPending[1] <= RecieveWriteMark;
case(Entry) // Flop to sample inputs case(Entry) // Flop to sample inputs
8'h00: Dout <= {20'b0, SckDiv}; SPI_SCKDIV: Dout <= {20'b0, SckDiv};
8'h04: Dout <= {30'b0, SckMode}; SPI_SCKMODE: Dout <= {30'b0, SckMode};
8'h10: Dout <= {30'b0, ChipSelectID}; SPI_CSID: Dout <= {30'b0, ChipSelectID};
8'h14: Dout <= {28'b0, ChipSelectDef}; SPI_CSDEF: Dout <= {28'b0, ChipSelectDef};
8'h18: Dout <= {30'b0, ChipSelectMode}; SPI_CSMODE: Dout <= {30'b0, ChipSelectMode};
8'h28: Dout <= {8'b0, Delay0[15:8], 8'b0, Delay0[7:0]}; SPI_DELAY0: Dout <= {8'b0, Delay0[15:8], 8'b0, Delay0[7:0]};
8'h2C: Dout <= {8'b0, Delay1[15:8], 8'b0, Delay1[7:0]}; SPI_DELAY1: Dout <= {8'b0, Delay1[15:8], 8'b0, Delay1[7:0]};
8'h40: Dout <= {12'b0, Format[4:1], 13'b0, Format[0], 2'b0}; SPI_FMT: Dout <= {12'b0, Format[4:1], 13'b0, Format[0], 2'b0};
8'h48: Dout <= {23'b0, TransmitFIFOWriteFull, 8'b0}; SPI_TXDATA: Dout <= {23'b0, TransmitFIFOWriteFull, 8'b0};
8'h4C: Dout <= {23'b0, ReceiveFIFOReadEmpty, ReceiveData[7:0]}; SPI_RXDATA: Dout <= {23'b0, ReceiveFIFOReadEmpty, ReceiveData[7:0]};
8'h50: Dout <= {29'b0, TransmitWatermark}; SPI_TXMARK: Dout <= {29'b0, TransmitWatermark};
8'h54: Dout <= {29'b0, ReceiveWatermark}; SPI_RXMARK: Dout <= {29'b0, ReceiveWatermark};
8'h70: Dout <= {30'b0, InterruptEnable}; SPI_IE: Dout <= {30'b0, InterruptEnable};
8'h74: Dout <= {30'b0, InterruptPending}; SPI_IP: Dout <= {30'b0, InterruptPending};
default: Dout <= 32'b0; default: Dout <= 32'b0;
endcase endcase
end end
@ -200,8 +216,8 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
assign SCLKenable = (DivCounter == SckDiv); assign SCLKenable = (DivCounter == SckDiv);
assign SCLKenableEarly = ((DivCounter + 12'b1) == SckDiv); assign SCLKenableEarly = ((DivCounter + 12'b1) == SckDiv);
always_ff @(posedge PCLK, negedge PRESETn) always_ff @(posedge PCLK, negedge PRESETn)
if (~PRESETn) DivCounter <= 0; if (~PRESETn) DivCounter <= '0;
else if (SCLKenable) DivCounter <= 0; else if (SCLKenable) DivCounter <= 12'b0;
else DivCounter <= DivCounter + 12'b1; else DivCounter <= DivCounter + 12'b1;
// Asserts when transmission is one frame before complete // Asserts when transmission is one frame before complete
@ -219,11 +235,11 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
// Calculate tx/rx fifo write and recieve increment signals // Calculate tx/rx fifo write and recieve increment signals
always_ff @(posedge PCLK, negedge PRESETn) always_ff @(posedge PCLK, negedge PRESETn)
if (~PRESETn) TransmitFIFOWriteIncrement <= 0; if (~PRESETn) TransmitFIFOWriteIncrement <= 1'b0;
else TransmitFIFOWriteIncrement <= (Memwrite & (Entry == 8'h48) & ~TransmitFIFOWriteFull & TransmitInactive); else TransmitFIFOWriteIncrement <= (Memwrite & (Entry == 8'h48) & ~TransmitFIFOWriteFull & TransmitInactive);
always_ff @(posedge PCLK, negedge PRESETn) always_ff @(posedge PCLK, negedge PRESETn)
if (~PRESETn) ReceiveFIFOReadIncrement <= 0; if (~PRESETn) ReceiveFIFOReadIncrement <= 1'b0;
else ReceiveFIFOReadIncrement <= ((Entry == 8'h4C) & ~ReceiveFIFOReadEmpty & PSEL & ~ReceiveFIFOReadIncrement); else ReceiveFIFOReadIncrement <= ((Entry == 8'h4C) & ~ReceiveFIFOReadEmpty & PSEL & ~ReceiveFIFOReadIncrement);
// Tx/Rx FIFOs // Tx/Rx FIFOs
@ -233,14 +249,14 @@ module spi_apb import cvw::*; #(parameter cvw_t P) (
ReceiveData[7:0], ReceiveFIFOWriteFull, ReceiveFIFOReadEmpty, RecieveWriteMark, RecieveReadMark); ReceiveData[7:0], ReceiveFIFOWriteFull, ReceiveFIFOReadEmpty, RecieveWriteMark, RecieveReadMark);
always_ff @(posedge PCLK, negedge PRESETn) always_ff @(posedge PCLK, negedge PRESETn)
if (~PRESETn) TransmitFIFOReadEmptyDelay <= 1; if (~PRESETn) TransmitFIFOReadEmptyDelay <= 1'b1;
else if (SCLKenable) TransmitFIFOReadEmptyDelay <= TransmitFIFOReadEmpty; else if (SCLKenable) TransmitFIFOReadEmptyDelay <= TransmitFIFOReadEmpty;
always_ff @(posedge PCLK, negedge PRESETn) always_ff @(posedge PCLK, negedge PRESETn)
if (~PRESETn) ReceiveShiftFullDelay <= 0; if (~PRESETn) ReceiveShiftFullDelay <= 1'b0;
else if (SCLKenable) ReceiveShiftFullDelay <= ReceiveShiftFull; else if (SCLKenable) ReceiveShiftFullDelay <= ReceiveShiftFull;
always_ff @(posedge PCLK, negedge PRESETn) always_ff @(posedge PCLK, negedge PRESETn)
if (~PRESETn) ReceiveShiftFullDelayPCLK <= 0; if (~PRESETn) ReceiveShiftFullDelayPCLK <= 1'b0;
else if (SCLKenableEarly) ReceiveShiftFullDelayPCLK <= ReceiveShiftFull; else if (SCLKenableEarly) ReceiveShiftFullDelayPCLK <= ReceiveShiftFull;
assign TransmitShiftRegLoad = ~TransmitShiftEmpty & ~Active | (((ChipSelectMode == 2'b10) & ~|(Delay1[15:8])) & ((ReceiveShiftFullDelay | ReceiveShiftFull) & ~SampleEdge & ~TransmitFIFOReadEmpty)); assign TransmitShiftRegLoad = ~TransmitShiftEmpty & ~Active | (((ChipSelectMode == 2'b10) & ~|(Delay1[15:8])) & ((ReceiveShiftFullDelay | ReceiveShiftFull) & ~SampleEdge & ~TransmitFIFOReadEmpty));
@ -399,12 +415,11 @@ module SynchFIFO #(parameter M=3, N=8)( // 2^M entries of N bits
// write and read are enabled // write and read are enabled
always_ff @(posedge PCLK, negedge PRESETn) always_ff @(posedge PCLK, negedge PRESETn)
if (~PRESETn) begin if (~PRESETn) begin
rptr <= 0; rptr <= '0;
wptr <= 0; wptr <= '0;
wfull <= 1'b0; wfull <= 1'b0;
rempty <= 1'b1; rempty <= 1'b1;
end end else begin
else begin
if (wen) begin if (wen) begin
wfull <= ({~wptrnext[M], wptrnext[M-1:0]} == rptr); wfull <= ({~wptrnext[M], wptrnext[M-1:0]} == rptr);
wptr <= wptrnext; wptr <= wptrnext;
@ -429,13 +444,13 @@ module TransmitShiftFSM(
output logic TransmitShiftEmpty); output logic TransmitShiftEmpty);
always_ff @(posedge PCLK, negedge PRESETn) always_ff @(posedge PCLK, negedge PRESETn)
if (~PRESETn) TransmitShiftEmpty <= 1; if (~PRESETn) TransmitShiftEmpty <= 1'b1;
else if (TransmitShiftEmpty) begin else if (TransmitShiftEmpty) begin
if (TransmitFIFOReadEmpty | (~TransmitFIFOReadEmpty & (ReceivePenultimateFrame & Active0))) TransmitShiftEmpty <= 1; if (TransmitFIFOReadEmpty | (~TransmitFIFOReadEmpty & (ReceivePenultimateFrame & Active0))) TransmitShiftEmpty <= 1'b1;
else if (~TransmitFIFOReadEmpty) TransmitShiftEmpty <= 0; else if (~TransmitFIFOReadEmpty) TransmitShiftEmpty <= 1'b0;
end else begin end else begin
if (ReceivePenultimateFrame & Active0) TransmitShiftEmpty <= 1; if (ReceivePenultimateFrame & Active0) TransmitShiftEmpty <= 1'b1;
else TransmitShiftEmpty <= 0; else TransmitShiftEmpty <= 1'b0;
end end
endmodule endmodule

View File

@ -52,6 +52,16 @@ module uartPC16550D #(parameter UART_PRESCALE) (
output logic SOUT, RTSb, DTRb, OUT1b, OUT2b // UART external serial and flow-control outputs output logic SOUT, RTSb, DTRb, OUT1b, OUT2b // UART external serial and flow-control outputs
); );
// register map
localparam UART_DLL_RBR = 3'b000;
localparam UART_DLM_IER = 3'b001;
localparam UART_IIR = 3'b010;
localparam UART_LCR = 3'b011;
localparam UART_MCR = 3'b100;
localparam UART_LSR = 3'b101;
localparam UART_MSR = 3'b110;
localparam UART_SCR = 3'b111;
// transmit and receive states // transmit and receive states
typedef enum logic [1:0] {UART_IDLE, UART_ACTIVE, UART_DONE, UART_BREAK} statetype; typedef enum logic [1:0] {UART_IDLE, UART_ACTIVE, UART_DONE, UART_BREAK} statetype;
@ -150,35 +160,35 @@ module uartPC16550D #(parameter UART_PRESCALE) (
if (~MEMWb) begin if (~MEMWb) begin
/* verilator lint_off CASEINCOMPLETE */ /* verilator lint_off CASEINCOMPLETE */
case (A) case (A)
3'b000: if (DLAB) DLL <= Din; // else TXHR <= Din; // TX handled in TX register/FIFO section UART_DLL_RBR: if (DLAB) DLL <= Din; // else TXHR <= Din; // TX handled in TX register/FIFO section
3'b001: if (DLAB) DLM <= Din; else IER <= Din[3:0]; UART_DLM_IER: if (DLAB) DLM <= Din; else IER <= Din[3:0];
3'b010: FCR <= {Din[7:6], 2'b0, Din[3], 2'b0, Din[0]}; // Write only FIFO Control Register; 4:5 reserved and 2:1 self-clearing UART_IIR: FCR <= {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 <= Din; UART_LCR: LCR <= Din;
3'b100: MCR <= Din[4:0]; UART_MCR: MCR <= Din[4:0];
3'b111: SCR <= Din; UART_SCR: SCR <= Din;
endcase endcase
/* verilator lint_on CASEINCOMPLETE */ /* verilator lint_on CASEINCOMPLETE */
end end
// Line Status Register (8.6.3) // Line Status Register (8.6.3)
// 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. // 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)) if (~MEMWb & (A == UART_LSR))
LSR[6:1] <= Din[6:1]; // recommended only for test, see 8.6.3 LSR[6:1] <= Din[6:1]; // recommended only for test, see 8.6.3
else begin else begin
LSR[0] <= rxdataready; // Data ready LSR[0] <= rxdataready; // Data ready
LSR[1] <= (LSR[1] | RXBR[10]) & ~squashRXerrIP;; // overrun error LSR[1] <= (LSR[1] | RXBR[10]) & ~squashRXerrIP; // overrun error
LSR[2] <= (LSR[2] | RXBR[9]) & ~squashRXerrIP; // parity error LSR[2] <= (LSR[2] | RXBR[9]) & ~squashRXerrIP; // parity error
LSR[3] <= (LSR[3] | RXBR[8]) & ~squashRXerrIP; // framing error LSR[3] <= (LSR[3] | RXBR[8]) & ~squashRXerrIP; // framing error
LSR[4] <= (LSR[4] | rxbreak) & ~squashRXerrIP; // break indicator LSR[4] <= (LSR[4] | rxbreak) & ~squashRXerrIP; // break indicator
LSR[5] <= THRE; // THRE LSR[5] <= THRE; // THRE
LSR[6] <= ~txsrfull & THRE; // TEMT LSR[6] <= ~txsrfull & THRE; // TEMT
if (rxfifohaserr) LSR[7] <= 1; // any bits in FIFO have error if (rxfifohaserr) LSR[7] <= 1'b1; // any bits in FIFO have error
end end
// Modem Status Register (8.6.8) // Modem Status Register (8.6.8)
if (~MEMWb & (A == 3'b110)) if (~MEMWb & (A == UART_MSR))
MSR <= Din[3:0]; MSR <= Din[3:0];
else if (~MEMRb & (A == 3'b110)) else if (~MEMRb & (A == UART_MSR))
MSR <= 4'b0; // Reading MSR clears the flags in MSR bits 3:0 MSR <= 4'b0; // Reading MSR clears the flags in MSR bits 3:0
else begin else begin
MSR[0] <= MSR[0] | CTSb2 ^ CTSbsync; // Delta Clear to Send MSR[0] <= MSR[0] | CTSb2 ^ CTSbsync; // Delta Clear to Send
@ -187,18 +197,18 @@ module uartPC16550D #(parameter UART_PRESCALE) (
MSR[3] <= MSR[3] | DCDb2 ^ DCDbsync; // Delta Data Carrier Detect MSR[3] <= MSR[3] | DCDb2 ^ DCDbsync; // Delta Data Carrier Detect
end end
end end
always_comb always_comb
if (~MEMRb) if (~MEMRb)
case (A) case (A)
3'b000: if (DLAB) Dout = DLL; else Dout = RBR[7:0]; UART_DLL_RBR: if (DLAB) Dout = DLL; else Dout = RBR[7:0];
3'b001: if (DLAB) Dout = DLM; else Dout = {4'b0, IER[3:0]}; UART_DLM_IER: if (DLAB) Dout = DLM; else Dout = {4'b0, IER[3:0]};
3'b010: Dout = {{2{fifoenabled}}, 2'b00, intrID[2:0], ~intrpending}; // Read only Interupt Ident Register UART_IIR: Dout = {{2{fifoenabled}}, 2'b00, intrID[2:0], ~intrpending}; // Read only Interupt Ident Register
3'b011: Dout = LCR; UART_LCR: Dout = LCR;
3'b100: Dout = {3'b000, MCR}; UART_MCR: Dout = {3'b000, MCR};
3'b101: Dout = LSR; UART_LSR: Dout = LSR;
// 3'b110: Dout = {~CTSbsync, ~DSRbsync, ~RIbsync, ~DCDbsync, MSR[3:0]}; UART_MSR: Dout = {~DCDbsync, ~RIbsync, ~DSRbsync, ~CTSbsync, MSR[3:0]};
3'b110: Dout = {~DCDbsync, ~RIbsync, ~DSRbsync, ~CTSbsync, MSR[3:0]}; UART_SCR: Dout = SCR;
3'b111: Dout = SCR;
endcase endcase
else Dout = 8'b0; else Dout = 8'b0;
@ -215,7 +225,7 @@ module uartPC16550D #(parameter UART_PRESCALE) (
always_ff @(posedge PCLK, negedge PRESETn) always_ff @(posedge PCLK, negedge PRESETn)
if (~PRESETn) begin if (~PRESETn) begin
baudcount <= 1; baudcount <= 1;
baudpulse <= 0; baudpulse <= 1'b0;
end else if (~MEMWb & DLAB & (A == 3'b0 | A == 3'b1)) begin end else if (~MEMWb & DLAB & (A == 3'b0 | A == 3'b1)) begin
baudcount <= 1; baudcount <= 1;
end else begin end else begin
@ -240,18 +250,18 @@ module uartPC16550D #(parameter UART_PRESCALE) (
always_ff @(posedge PCLK, negedge PRESETn) always_ff @(posedge PCLK, negedge PRESETn)
if (~PRESETn) begin if (~PRESETn) begin
rxoversampledcnt <= 0; rxoversampledcnt <= '0;
rxstate <= UART_IDLE; rxstate <= UART_IDLE;
rxbitsreceived <= 0; rxbitsreceived <= '0;
rxtimeoutcnt <= 0; rxtimeoutcnt <= '0;
end else begin end else begin
if (rxstate == UART_IDLE & ~SINsync) begin // got start bit if (rxstate == UART_IDLE & ~SINsync) begin // got start bit
rxstate <= UART_ACTIVE; rxstate <= UART_ACTIVE;
rxoversampledcnt <= 0; rxoversampledcnt <= '0;
rxbitsreceived <= 0; rxbitsreceived <= '0;
if (~rxfifotimeout) rxtimeoutcnt <= 0; // reset timeout when new character is arriving. Jacob Pease: Only if the timeout was not already reached. p.16 PC16550D.pdf if (~rxfifotimeout) rxtimeoutcnt <= '0; // reset timeout when new character is arriving. Jacob Pease: Only if the timeout was not already reached. p.16 PC16550D.pdf
end else if (rxbaudpulse & (rxstate == UART_ACTIVE)) begin end else if (rxbaudpulse & (rxstate == UART_ACTIVE)) begin
rxoversampledcnt <= rxoversampledcnt + 1; // 16x oversampled counter rxoversampledcnt <= rxoversampledcnt + 4'b1; // 16x oversampled counter
if (rxcentered) rxbitsreceived <= rxbitsreceived + 1; if (rxcentered) rxbitsreceived <= rxbitsreceived + 1;
if (rxbitsreceived == rxbitsexpected) rxstate <= UART_DONE; // pulse rxdone for a cycle if (rxbitsreceived == rxbitsexpected) rxstate <= UART_DONE; // pulse rxdone for a cycle
end else if (rxstate == UART_DONE | rxstate == UART_BREAK) begin end else if (rxstate == UART_DONE | rxstate == UART_BREAK) begin
@ -259,7 +269,7 @@ module uartPC16550D #(parameter UART_PRESCALE) (
else rxstate <= UART_IDLE; else rxstate <= UART_IDLE;
end end
// timeout counting // timeout counting
if (~MEMRb & A == 3'b000 & ~DLAB) rxtimeoutcnt <= 0; // reset timeout on read if (~MEMRb & A == 3'b000 & ~DLAB) rxtimeoutcnt <= '0; // reset timeout on read
else if (fifoenabled & ~rxfifoempty & rxbaudpulse & ~rxfifotimeout) rxtimeoutcnt <= rxtimeoutcnt+1; // may not be right else if (fifoenabled & ~rxfifoempty & rxbaudpulse & ~rxfifotimeout) rxtimeoutcnt <= rxtimeoutcnt+1; // may not be right
end end
@ -295,32 +305,32 @@ module uartPC16550D #(parameter UART_PRESCALE) (
// receive FIFO and register // receive FIFO and register
always_ff @(posedge PCLK) always_ff @(posedge PCLK)
if (~PRESETn) begin if (~PRESETn) begin
rxfifohead <= 0; rxfifotail <= 0; rxdataready <= 0; RXBR <= 0; rxfifohead <= '0; rxfifotail <= '0; rxdataready <= 1'b0; RXBR <= '0;
end else begin end else begin
if (~MEMWb & (A == 3'b010) & Din[1]) begin if (~MEMWb & (A == 3'b010) & Din[1]) begin
rxfifohead <= 0; rxfifotail <= 0; rxdataready <= 0; rxfifohead <= '0; rxfifotail <= '0; rxdataready <= 1'b0;
end else if (rxstate == UART_DONE) begin end else if (rxstate == UART_DONE) begin
RXBR <= {rxoverrunerr, rxparityerr, rxframingerr, rxdata}; // load recevive buffer register RXBR <= {rxoverrunerr, rxparityerr, rxframingerr, rxdata}; // load recevive buffer register
if (rxoverrunerr) $warning("UART RX Overrun Err\n"); // if (rxoverrunerr) $warning("UART RX Overrun Err\n");
if (rxparityerr) $warning("UART RX Parity Err\n"); // if (rxparityerr) $warning("UART RX Parity Err\n");
if (rxframingerr) $warning("UART RX Framing Err\n"); // if (rxframingerr) $warning("UART RX Framing Err\n");
if (fifoenabled) begin if (fifoenabled) begin
rxfifo[rxfifohead] <= {rxoverrunerr, rxparityerr, rxframingerr, rxdata}; rxfifo[rxfifohead] <= {rxoverrunerr, rxparityerr, rxframingerr, rxdata};
rxfifohead <= rxfifohead + 1; rxfifohead <= rxfifohead + 1'b1;
end end
rxdataready <= 1; rxdataready <= 1'b1;
end else if (~MEMRb & A == 3'b000 & ~DLAB) begin // reading RBR updates ready / pops fifo end else if (~MEMRb & A == 3'b000 & ~DLAB) begin // reading RBR updates ready / pops fifo
if (fifoenabled) begin if (fifoenabled) begin
if (~rxfifoempty) rxfifotail <= rxfifotail + 1; if (~rxfifoempty) rxfifotail <= rxfifotail + 1;
// if (rxfifoempty) rxdataready <= 0; // if (rxfifoempty) rxdataready <= 1'b0;
if (rxfifoentries == 1) rxdataready <= 0; // When reading the last entry, data ready becomes zero if (rxfifoentries == 1) rxdataready <= 1'b0; // When reading the last entry, data ready becomes zero
end else begin end else begin
rxdataready <= 0; rxdataready <= 1'b0;
RXBR <= {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) RXBR <= {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)
end end
end else if (~MEMWb & A == 3'b010) // writes to FIFO Control Register end else if (~MEMWb & A == 3'b010) // writes to FIFO Control Register
if (Din[1] | ~Din[0]) begin // rx FIFO reset or FIFO disable clears FIFO contents if (Din[1] | ~Din[0]) begin // rx FIFO reset or FIFO disable clears FIFO contents
rxfifohead <= 0; rxfifotail <= 0; rxfifohead <= '0; rxfifotail <= '0;
end end
end end
@ -354,9 +364,9 @@ module uartPC16550D #(parameter UART_PRESCALE) (
// receive buffer register and ready bit // receive buffer register and ready bit
always_ff @(posedge PCLK, negedge PRESETn) // track rxrdy for DMA mode (FCR3 = FCR0 = 1) always_ff @(posedge PCLK, negedge PRESETn) // track rxrdy for DMA mode (FCR3 = FCR0 = 1)
if (~PRESETn) rxfifodmaready <= 0; if (~PRESETn) rxfifodmaready <= 1'b0;
else if (rxfifotriggered | rxfifotimeout) rxfifodmaready <= 1; else if (rxfifotriggered | rxfifotimeout) rxfifodmaready <= 1'b1;
else if (rxfifoempty) rxfifodmaready <= 0; else if (rxfifoempty) rxfifodmaready <= 1'b0;
always_comb always_comb
if (fifoenabled) begin if (fifoenabled) begin
@ -375,17 +385,17 @@ module uartPC16550D #(parameter UART_PRESCALE) (
always_ff @(posedge PCLK, negedge PRESETn) always_ff @(posedge PCLK, negedge PRESETn)
if (~PRESETn) begin if (~PRESETn) begin
txoversampledcnt <= 0; txoversampledcnt <= '0;
txstate <= UART_IDLE; txstate <= UART_IDLE;
txbitssent <= 0; txbitssent <= '0;
end else if ((txstate == UART_IDLE) & txsrfull) begin // start transmitting end else if ((txstate == UART_IDLE) & txsrfull) begin // start transmitting
txstate <= UART_ACTIVE; txstate <= UART_ACTIVE;
txoversampledcnt <= 1; txoversampledcnt <= 4'b1;
txbitssent <= 0; txbitssent <= '0;
end else if (txbaudpulse & (txstate == UART_ACTIVE)) begin end else if (txbaudpulse & (txstate == UART_ACTIVE)) begin
txoversampledcnt <= txoversampledcnt + 1; txoversampledcnt <= txoversampledcnt + 1'b1;
if (txnextbit) begin // transmit at end of phase if (txnextbit) begin // transmit at end of phase
txbitssent <= txbitssent+1; txbitssent <= txbitssent + 1'b1;
if (txbitssent == txbitsexpected) txstate <= UART_DONE; if (txbitssent == txbitsexpected) txstate <= UART_DONE;
end end
end else if (txstate == UART_DONE) begin end else if (txstate == UART_DONE) begin
@ -423,17 +433,17 @@ module uartPC16550D #(parameter UART_PRESCALE) (
// registers & FIFO // registers & FIFO
always_ff @(posedge PCLK, negedge PRESETn) always_ff @(posedge PCLK, negedge PRESETn)
if (~PRESETn) begin if (~PRESETn) begin
txfifohead <= 0; txfifotail <= 0; txhrfull <= 0; txsrfull <= 0; TXHR <= 0; txsr <= 12'hfff; txfifohead <= '0; txfifotail <= '0; txhrfull <= 1'b0; txsrfull <= 1'b0; TXHR <= '0; txsr <= 12'hfff;
end else if (~MEMWb & (A == 3'b010) & Din[2]) begin end else if (~MEMWb & (A == 3'b010) & Din[2]) begin
txfifohead <= 0; txfifotail <= 0; txfifohead <= '0; txfifotail <= '0;
end else begin end else begin
if (~MEMWb & A == 3'b000 & ~DLAB) begin // writing transmit holding register or fifo if (~MEMWb & A == 3'b000 & ~DLAB) begin // writing transmit holding register or fifo
if (fifoenabled) begin if (fifoenabled) begin
txfifo[txfifohead] <= Din; txfifo[txfifohead] <= Din;
txfifohead <= txfifohead + 1; txfifohead <= txfifohead + 4'b1;
end else begin end else begin
TXHR <= Din; TXHR <= Din;
txhrfull <= 1; txhrfull <= 1'b1;
end end
$write("%c",Din); // for testbench $write("%c",Din); // for testbench
end end
@ -442,18 +452,18 @@ module uartPC16550D #(parameter UART_PRESCALE) (
if (~txfifoempty & ~txsrfull) begin if (~txfifoempty & ~txsrfull) begin
txsr <= txdata; txsr <= txdata;
txfifotail <= txfifotail+1; txfifotail <= txfifotail+1;
txsrfull <= 1; txsrfull <= 1'b1;
end end
end else if (txhrfull) begin end else if (txhrfull) begin
txsr <= txdata; txsr <= txdata;
txhrfull <= 0; txhrfull <= 1'b0;
txsrfull <= 1; txsrfull <= 1'b1;
end end
end else if (txstate == UART_DONE) txsrfull <= 0; // done transmitting shift register end else if (txstate == UART_DONE) txsrfull <= 1'b0; // done transmitting shift register
else if (txstate == UART_ACTIVE & txnextbit) txsr <= {txsr[10:0], 1'b1}; // shift txhr else if (txstate == UART_ACTIVE & txnextbit) txsr <= {txsr[10:0], 1'b1}; // shift txhr
if (!MEMWb & A == 3'b010) // writes to FIFO control register if (!MEMWb & A == 3'b010) // writes to FIFO control register
if (Din[2] | ~Din[0]) begin // tx FIFO reste or FIFO disable clears FIFO contents if (Din[2] | ~Din[0]) begin // tx FIFO reste or FIFO disable clears FIFO contents
txfifohead <= 0; txfifotail <= 0; txfifohead <= '0; txfifotail <= '0;
end end
end end
@ -483,9 +493,9 @@ module uartPC16550D #(parameter UART_PRESCALE) (
// transmit buffer ready bit // transmit buffer ready bit
always_ff @(posedge PCLK, negedge PRESETn) // track txrdy for DMA mode (FCR3 = FCR0 = 1) always_ff @(posedge PCLK, negedge PRESETn) // track txrdy for DMA mode (FCR3 = FCR0 = 1)
if (~PRESETn) txfifodmaready <= 0; if (~PRESETn) txfifodmaready <= 1'b0;
else if (txfifoempty) txfifodmaready <= 1; else if (txfifoempty) txfifodmaready <= 1'b1;
else if (txfifofull) txfifodmaready <= 0; else if (txfifofull) txfifodmaready <= 1'b0;
always_comb always_comb
if (fifoenabled & fifodmamodesel) TXRDYb = ~txfifodmaready; if (fifoenabled & fifodmamodesel) TXRDYb = ~txfifodmaready;
@ -493,7 +503,7 @@ module uartPC16550D #(parameter UART_PRESCALE) (
// Transmitter pin // Transmitter pin
assign SOUTbit = txsr[11]; // transmit most significant bit assign SOUTbit = txsr[11]; // transmit most significant bit
assign SOUT = loop ? 1 : (LCR[6] ? 0 : SOUTbit); // tied to 1 during loopback or 0 during break assign SOUT = loop ? 1 : (LCR[6] ? '0 : SOUTbit); // tied to 1 during loopback or 0 during break
/////////////////////////////////////////// ///////////////////////////////////////////
// interrupts // interrupts
@ -509,7 +519,7 @@ module uartPC16550D #(parameter UART_PRESCALE) (
// IIR: interrupt priority (Table 5) // IIR: interrupt priority (Table 5)
// set intrID based on highest priority pending interrupt source; otherwise, no interrupt is pending // set intrID based on highest priority pending interrupt source; otherwise, no interrupt is pending
always_comb begin always_comb begin
intrpending = 1; intrpending = 1'b1;
if (RXerrIP & IER[2]) intrID = 3'b011; if (RXerrIP & IER[2]) intrID = 3'b011;
else if (rxdataavailintr & IER[0]) intrID = 3'b010; else if (rxdataavailintr & IER[0]) intrID = 3'b010;
else if (rxfifotimeout & fifoenabled & IER[0]) intrID = 3'b110; else if (rxfifotimeout & fifoenabled & IER[0]) intrID = 3'b110;
@ -517,7 +527,7 @@ module uartPC16550D #(parameter UART_PRESCALE) (
else if (modemstatusintr & IER[3]) intrID = 3'b000; else if (modemstatusintr & IER[3]) intrID = 3'b000;
else begin else begin
intrID = 3'b000; intrID = 3'b000;
intrpending = 0; intrpending = 1'b0;
end end
end end
always_ff @(posedge PCLK) INTR <= intrpending; // prevent glitches on interrupt pin always_ff @(posedge PCLK) INTR <= intrpending; // prevent glitches on interrupt pin
@ -549,10 +559,10 @@ module uartPC16550D #(parameter UART_PRESCALE) (
assign fifodmamodesel = FCR[3]; assign fifodmamodesel = FCR[3];
always_comb always_comb
case (FCR[7:6]) case (FCR[7:6])
2'b00: rxfifotriggerlevel = 1; 2'b00: rxfifotriggerlevel = 4'd1;
2'b01: rxfifotriggerlevel = 4; 2'b01: rxfifotriggerlevel = 4'd4;
2'b10: rxfifotriggerlevel = 8; 2'b10: rxfifotriggerlevel = 4'd8;
2'b11: rxfifotriggerlevel = 14; 2'b11: rxfifotriggerlevel = 4'd14;
endcase endcase
endmodule endmodule