首次提交
This commit is contained in:
100
new_project_rtl_template/sim/apb_uart/apb_uart_interrupt.sv
Normal file
100
new_project_rtl_template/sim/apb_uart/apb_uart_interrupt.sv
Normal file
@@ -0,0 +1,100 @@
|
||||
// Copyright 2017 ETH Zurich and University of Bologna.
|
||||
// Copyright and related rights are licensed under the Solderpad Hardware
|
||||
// License, Version 0.51 (the “License”); you may not use this file except in
|
||||
// compliance with the License. You may obtain a copy of the License at
|
||||
// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
|
||||
// or agreed to in writing, software, hardware and materials distributed under
|
||||
// this 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.
|
||||
|
||||
module apb_uart_interrupt
|
||||
#(
|
||||
parameter TX_FIFO_DEPTH = 32,
|
||||
parameter RX_FIFO_DEPTH = 32
|
||||
)
|
||||
(
|
||||
input logic clk_i,
|
||||
input logic rstn_i,
|
||||
|
||||
// registers
|
||||
input logic [7:0] IER_i, // interrupt enable register
|
||||
input logic RDA_i, // receiver data available
|
||||
input logic overrun_i, // character timeout indication
|
||||
|
||||
// control logic
|
||||
input logic error_i,
|
||||
input logic [$clog2(RX_FIFO_DEPTH):0] rx_elements_i,
|
||||
input logic [$clog2(TX_FIFO_DEPTH):0] tx_elements_i,
|
||||
input logic [1:0] trigger_level_i,
|
||||
|
||||
input logic [3:0] clr_int_i, // one hot
|
||||
|
||||
output logic interrupt_o,
|
||||
output logic [3:0] IIR_o
|
||||
);
|
||||
|
||||
logic [3:0] iir_n, iir_q;
|
||||
logic trigger_level_reached;
|
||||
|
||||
always_comb
|
||||
begin
|
||||
trigger_level_reached = 1'b0;
|
||||
case (trigger_level_i)
|
||||
2'b00:
|
||||
if ($unsigned(rx_elements_i) == 1)
|
||||
trigger_level_reached = 1'b1;
|
||||
2'b01:
|
||||
if ($unsigned(rx_elements_i) == 4)
|
||||
trigger_level_reached = 1'b1;
|
||||
2'b10:
|
||||
if ($unsigned(rx_elements_i) == 8)
|
||||
trigger_level_reached = 1'b1;
|
||||
2'b11:
|
||||
if ($unsigned(rx_elements_i) == 14)
|
||||
trigger_level_reached = 1'b1;
|
||||
default : /* default */;
|
||||
endcase
|
||||
end
|
||||
|
||||
always_comb
|
||||
begin
|
||||
iir_n = iir_q;
|
||||
if (clr_int_i == iir_q)
|
||||
iir_n = 4'b0001;
|
||||
|
||||
if (iir_q == 4'b0100 && (!trigger_level_reached && !RDA_i))
|
||||
iir_n = 4'b0001;
|
||||
|
||||
// Parity error
|
||||
if (IER_i[2] & error_i)
|
||||
iir_n = 4'b0110;
|
||||
// Received data available or trigger level reached in FIFO mode
|
||||
else if (IER_i[0] & (trigger_level_reached | RDA_i))
|
||||
iir_n = 4'b0100;
|
||||
// Overrun error
|
||||
else if (IER_i[4] & overrun_i)
|
||||
iir_n = 4'b1100;
|
||||
// Transmitter holding register empty
|
||||
else if (IER_i[1] & tx_elements_i == 0)
|
||||
iir_n = 4'b0010;
|
||||
end
|
||||
|
||||
|
||||
always_ff @(posedge clk_i, negedge rstn_i)
|
||||
begin
|
||||
if (~rstn_i)
|
||||
begin
|
||||
iir_q <= 4'b0001;
|
||||
end
|
||||
else
|
||||
begin
|
||||
iir_q <= iir_n;
|
||||
end
|
||||
end
|
||||
|
||||
assign IIR_o = iir_q;
|
||||
assign interrupt_o = ~iir_q[0];
|
||||
|
||||
endmodule
|
||||
|
||||
199
new_project_rtl_template/sim/apb_uart/apb_uart_rx.sv
Normal file
199
new_project_rtl_template/sim/apb_uart/apb_uart_rx.sv
Normal file
@@ -0,0 +1,199 @@
|
||||
|
||||
module apb_uart_rx (
|
||||
input logic clk_i,
|
||||
input logic rstn_i,
|
||||
input logic rx_i,
|
||||
input logic [15:0] cfg_div_i,
|
||||
input logic cfg_en_i,
|
||||
input logic cfg_parity_en_i,
|
||||
input logic cfg_even_parity_i,
|
||||
input logic [1:0] cfg_bits_i,
|
||||
// input logic cfg_stop_bits_i,
|
||||
output logic busy_o,
|
||||
output logic parity_error_o,
|
||||
output logic overrun_o,
|
||||
input logic err_clr_i,
|
||||
output logic [7:0] rx_data_o,
|
||||
output logic rx_valid_o,
|
||||
input logic rx_ready_i
|
||||
);
|
||||
|
||||
enum logic [2:0] {IDLE,START_BIT,DATA,PARITY,STOP_BIT} CS, NS;
|
||||
|
||||
logic [7:0] reg_data;
|
||||
|
||||
logic [2:0] reg_rx_sync;
|
||||
|
||||
|
||||
logic [2:0] reg_bit_count;
|
||||
|
||||
logic [2:0] s_target_bits;
|
||||
|
||||
logic parity_bit;
|
||||
|
||||
logic [15:0] baud_cnt;
|
||||
logic baudgen_en;
|
||||
logic bit_done;
|
||||
|
||||
logic start_bit;
|
||||
logic s_rx_fall;
|
||||
|
||||
|
||||
assign busy_o = (CS != IDLE);
|
||||
|
||||
always_comb begin
|
||||
case(cfg_bits_i)
|
||||
2'b00:
|
||||
s_target_bits = 3'h4;
|
||||
2'b01:
|
||||
s_target_bits = 3'h5;
|
||||
2'b10:
|
||||
s_target_bits = 3'h6;
|
||||
2'b11:
|
||||
s_target_bits = 3'h7;
|
||||
endcase
|
||||
end
|
||||
|
||||
always_comb begin
|
||||
NS = CS;
|
||||
baudgen_en = 1'b0;
|
||||
start_bit = 1'b0;
|
||||
|
||||
case(CS)
|
||||
IDLE: begin
|
||||
if (s_rx_fall) begin
|
||||
NS = START_BIT;
|
||||
baudgen_en = 1'b1;
|
||||
start_bit = 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
START_BIT: begin
|
||||
baudgen_en = 1'b1;
|
||||
start_bit = 1'b1;
|
||||
if (bit_done)
|
||||
NS = DATA;
|
||||
end
|
||||
|
||||
DATA: begin
|
||||
baudgen_en = 1'b1;
|
||||
|
||||
if (bit_done) begin
|
||||
if (reg_bit_count == s_target_bits) begin
|
||||
if (cfg_parity_en_i)
|
||||
NS = PARITY;
|
||||
else
|
||||
NS = STOP_BIT;
|
||||
end
|
||||
end
|
||||
end
|
||||
PARITY: begin
|
||||
baudgen_en = 1'b1;
|
||||
if (bit_done) begin
|
||||
NS = STOP_BIT;
|
||||
end
|
||||
end
|
||||
STOP_BIT: begin
|
||||
baudgen_en = 1'b1;
|
||||
if (bit_done) begin
|
||||
NS = IDLE;
|
||||
end
|
||||
end
|
||||
default:
|
||||
NS = IDLE;
|
||||
endcase
|
||||
end
|
||||
|
||||
always_ff @(posedge clk_i or negedge rstn_i)
|
||||
begin
|
||||
if (rstn_i == 1'b0) begin
|
||||
CS <= IDLE;
|
||||
reg_data <= 8'hFF;
|
||||
reg_bit_count <= 'h0;
|
||||
parity_bit <= 1'b0;
|
||||
parity_error_o <= 1'b0;
|
||||
rx_valid_o <= 1'b0;
|
||||
overrun_o <= 1'b0;
|
||||
end else begin
|
||||
if(cfg_en_i)
|
||||
CS <= NS;
|
||||
else
|
||||
CS <= IDLE;
|
||||
|
||||
rx_valid_o <= 0;
|
||||
case (CS)
|
||||
START_BIT:
|
||||
parity_bit <= ~cfg_even_parity_i;
|
||||
DATA:
|
||||
if (bit_done) begin
|
||||
parity_bit <= parity_bit ^ reg_rx_sync[2];
|
||||
case(cfg_bits_i)
|
||||
2'b00:
|
||||
reg_data <= {3'b000,reg_rx_sync[2],reg_data[4:1]};
|
||||
2'b01:
|
||||
reg_data <= {2'b00,reg_rx_sync[2],reg_data[5:1]};
|
||||
2'b10:
|
||||
reg_data <= {1'b0,reg_rx_sync[2],reg_data[6:1]};
|
||||
2'b11:
|
||||
reg_data <= {reg_rx_sync[2],reg_data[7:1]};
|
||||
endcase
|
||||
|
||||
if (reg_bit_count == s_target_bits)
|
||||
reg_bit_count <= 'h0;
|
||||
else
|
||||
reg_bit_count <= reg_bit_count + 1;
|
||||
end
|
||||
PARITY:
|
||||
if (bit_done)
|
||||
if(parity_bit != reg_rx_sync[2])
|
||||
parity_error_o <= 1'b1;
|
||||
else
|
||||
parity_error_o <= 1'b0;
|
||||
STOP_BIT:
|
||||
if (bit_done) begin
|
||||
rx_valid_o <= 1'b1;
|
||||
overrun_o <= ~rx_ready_i;
|
||||
end
|
||||
endcase
|
||||
end
|
||||
end
|
||||
|
||||
assign s_rx_fall = ~reg_rx_sync[1] & reg_rx_sync[2];
|
||||
|
||||
always_ff @(posedge clk_i or negedge rstn_i) begin
|
||||
if (rstn_i == 1'b0)
|
||||
reg_rx_sync <= 3'b111;
|
||||
else begin
|
||||
if (cfg_en_i)
|
||||
reg_rx_sync <= {reg_rx_sync[1:0],rx_i};
|
||||
else
|
||||
reg_rx_sync <= 3'b111;
|
||||
end
|
||||
end
|
||||
|
||||
always_ff @(posedge clk_i or negedge rstn_i) begin
|
||||
if (rstn_i == 1'b0) begin
|
||||
baud_cnt <= 'h0;
|
||||
bit_done <= 1'b0;
|
||||
end else begin
|
||||
if(baudgen_en) begin
|
||||
if(!start_bit && (baud_cnt == cfg_div_i)) begin
|
||||
baud_cnt <= 'h0;
|
||||
bit_done <= 1'b1;
|
||||
end else if(start_bit && (baud_cnt == {1'b0,cfg_div_i[15:1]})) begin
|
||||
baud_cnt <= 'h0;
|
||||
bit_done <= 1'b1;
|
||||
end else begin
|
||||
baud_cnt <= baud_cnt + 1;
|
||||
bit_done <= 1'b0;
|
||||
end
|
||||
end else begin
|
||||
baud_cnt <= 'h0;
|
||||
bit_done <= 1'b0;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
assign rx_data_o = reg_data;
|
||||
|
||||
endmodule
|
||||
362
new_project_rtl_template/sim/apb_uart/apb_uart_top.sv
Normal file
362
new_project_rtl_template/sim/apb_uart/apb_uart_top.sv
Normal file
@@ -0,0 +1,362 @@
|
||||
// Copyright 2017 ETH Zurich and University of Bologna.
|
||||
// Copyright and related rights are licensed under the Solderpad Hardware
|
||||
// License, Version 0.51 (the “License”); you may not use this file except in
|
||||
// compliance with the License. You may obtain a copy of the License at
|
||||
// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
|
||||
// or agreed to in writing, software, hardware and materials distributed under
|
||||
// this 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.
|
||||
|
||||
module apb_uart_top
|
||||
#(
|
||||
parameter CLOCK_FREQUENCY = 50e6,
|
||||
parameter BAUD_RATE = 115200,
|
||||
parameter APB_ADDR_WIDTH = 12 //APB slaves are 4KB by default
|
||||
)
|
||||
(
|
||||
input logic CLK,
|
||||
input logic RSTN,
|
||||
/* verilator lint_off UNUSED */
|
||||
input logic [APB_ADDR_WIDTH-1:0] PADDR,
|
||||
/* lint_on */
|
||||
input logic [31:0] PWDATA,
|
||||
input logic PWRITE,
|
||||
input logic PSEL,
|
||||
input logic PENABLE,
|
||||
output logic [31:0] PRDATA,
|
||||
output logic PREADY,
|
||||
output logic PSLVERR,
|
||||
|
||||
input logic rx_i, // Receiver input
|
||||
output logic tx_o, // Transmitter output
|
||||
|
||||
output logic rda_o, // rx data avaiable
|
||||
output logic tde_o, // tx data empty
|
||||
output logic event_o // interrupt/event output
|
||||
);
|
||||
// register addresses
|
||||
parameter RBR = 3'h0, THR = 3'h0, DLL = 3'h0, IER = 3'h1, DLM = 3'h1, IIR = 3'h2,
|
||||
FCR = 3'h2, LCR = 3'h3, MCR = 3'h4, LSR = 3'h5, MSR = 3'h6, SCR = 3'h7;
|
||||
|
||||
parameter TX_FIFO_DEPTH = 16; // in bytes
|
||||
parameter RX_FIFO_DEPTH = 16; // in bytes
|
||||
|
||||
logic [2:0] register_adr;
|
||||
logic [9:0][7:0] regs_q, regs_n;
|
||||
logic [1:0] trigger_level_n, trigger_level_q;
|
||||
|
||||
// receive buffer register, read only
|
||||
logic [7:0] rx_data;
|
||||
// parity error
|
||||
logic parity_error;
|
||||
logic rx_overrun;
|
||||
logic [3:0] IIR_o;
|
||||
logic [3:0] clr_int;
|
||||
|
||||
/* verilator lint_off UNOPTFLAT */
|
||||
// tx flow control
|
||||
logic tx_ready;
|
||||
/* lint_on */
|
||||
|
||||
// rx flow control
|
||||
logic apb_rx_ready;
|
||||
logic rx_valid;
|
||||
|
||||
logic tx_fifo_clr_n, tx_fifo_clr_q;
|
||||
logic rx_fifo_clr_n, rx_fifo_clr_q;
|
||||
|
||||
logic fifo_tx_valid;
|
||||
logic tx_valid;
|
||||
logic fifo_rx_valid;
|
||||
logic fifo_rx_ready;
|
||||
logic rx_ready;
|
||||
|
||||
logic [7:0] fifo_tx_data;
|
||||
logic [8:0] fifo_rx_data;
|
||||
|
||||
logic [7:0] tx_data;
|
||||
logic [$clog2(TX_FIFO_DEPTH):0] tx_elements;
|
||||
logic [$clog2(RX_FIFO_DEPTH):0] rx_elements;
|
||||
|
||||
// TODO: check that stop bits are really not necessary here
|
||||
apb_uart_rx apb_uart_rx_i
|
||||
(
|
||||
.clk_i ( CLK ),
|
||||
.rstn_i ( RSTN ),
|
||||
.rx_i ( rx_i ),
|
||||
.cfg_en_i ( 1'b1 ),
|
||||
.cfg_div_i ( {regs_q[DLM + 'd8], regs_q[DLL + 'd8]} ),
|
||||
.cfg_parity_en_i ( regs_q[LCR][3] ),
|
||||
.cfg_even_parity_i ( regs_q[LCR][4] ),
|
||||
.cfg_bits_i ( regs_q[LCR][1:0] ),
|
||||
// .cfg_stop_bits_i ( regs_q[LCR][2] ),
|
||||
/* verilator lint_off PINCONNECTEMPTY */
|
||||
.busy_o ( ),
|
||||
/* lint_on */
|
||||
.parity_error_o ( parity_error ),
|
||||
.overrun_o ( rx_overrun ),
|
||||
.err_clr_i ( 1'b1 ),
|
||||
.rx_data_o ( rx_data ),
|
||||
.rx_valid_o ( rx_valid ),
|
||||
.rx_ready_i ( rx_ready )
|
||||
);
|
||||
|
||||
apb_uart_tx apb_uart_tx_i
|
||||
(
|
||||
.clk_i ( CLK ),
|
||||
.rstn_i ( RSTN ),
|
||||
.tx_o ( tx_o ),
|
||||
/* verilator lint_off PINCONNECTEMPTY */
|
||||
.busy_o ( ),
|
||||
/* lint_on */
|
||||
.cfg_en_i ( 1'b1 ),
|
||||
.cfg_div_i ( {regs_q[DLM + 'd8], regs_q[DLL + 'd8]} ),
|
||||
.cfg_parity_en_i ( regs_q[LCR][3] ),
|
||||
.cfg_even_parity_i ( regs_q[LCR][4] ),
|
||||
.cfg_bits_i ( regs_q[LCR][1:0] ),
|
||||
.cfg_stop_bits_i ( regs_q[LCR][2] ),
|
||||
|
||||
.tx_data_i ( tx_data ),
|
||||
.tx_valid_i ( tx_valid ),
|
||||
.tx_ready_o ( tx_ready )
|
||||
);
|
||||
|
||||
io_generic_fifo
|
||||
#(
|
||||
.DATA_WIDTH ( 9 ),
|
||||
.BUFFER_DEPTH ( RX_FIFO_DEPTH )
|
||||
)
|
||||
apb_uart_rx_fifo_i
|
||||
(
|
||||
.clk_i ( CLK ),
|
||||
.rstn_i ( RSTN ),
|
||||
|
||||
.clr_i ( rx_fifo_clr_q ),
|
||||
|
||||
.elements_o ( rx_elements ),
|
||||
|
||||
.data_o ( fifo_rx_data ),
|
||||
.valid_o ( fifo_rx_valid ),
|
||||
.ready_i ( fifo_rx_ready ),
|
||||
|
||||
.valid_i ( rx_valid ),
|
||||
.data_i ( { parity_error, rx_data } ),
|
||||
.ready_o ( rx_ready )
|
||||
);
|
||||
|
||||
io_generic_fifo
|
||||
#(
|
||||
.DATA_WIDTH ( 8 ),
|
||||
.BUFFER_DEPTH ( TX_FIFO_DEPTH )
|
||||
)
|
||||
apb_uart_tx_fifo_i
|
||||
(
|
||||
.clk_i ( CLK ),
|
||||
.rstn_i ( RSTN ),
|
||||
|
||||
.clr_i ( tx_fifo_clr_q ),
|
||||
|
||||
.elements_o ( tx_elements ),
|
||||
|
||||
.data_o ( tx_data ),
|
||||
.valid_o ( tx_valid ),
|
||||
.ready_i ( tx_ready ),
|
||||
|
||||
.valid_i ( fifo_tx_valid ),
|
||||
.data_i ( fifo_tx_data ),
|
||||
// not needed since we are getting the status via the fifo population
|
||||
.ready_o ( )
|
||||
);
|
||||
|
||||
apb_uart_interrupt
|
||||
#(
|
||||
.TX_FIFO_DEPTH (TX_FIFO_DEPTH),
|
||||
.RX_FIFO_DEPTH (RX_FIFO_DEPTH)
|
||||
)
|
||||
apb_uart_interrupt_i
|
||||
(
|
||||
.clk_i ( CLK ),
|
||||
.rstn_i ( RSTN ),
|
||||
|
||||
|
||||
.IER_i ( regs_q[IER] ), // interrupt enable register
|
||||
.RDA_i ( regs_n[LSR][0] ), // receiver data available
|
||||
.overrun_i ( regs_n[LSR][1] ), // rx data overrun
|
||||
|
||||
|
||||
.error_i ( regs_n[LSR][2] ),
|
||||
.rx_elements_i ( rx_elements ),
|
||||
.tx_elements_i ( tx_elements ),
|
||||
.trigger_level_i ( trigger_level_q ),
|
||||
|
||||
.clr_int_i ( clr_int ), // one hot
|
||||
|
||||
.interrupt_o ( event_o ),
|
||||
.IIR_o ( IIR_o )
|
||||
|
||||
);
|
||||
|
||||
// UART Registers
|
||||
|
||||
// register write and update logic
|
||||
always_comb
|
||||
begin
|
||||
regs_n = regs_q;
|
||||
trigger_level_n = trigger_level_q;
|
||||
|
||||
fifo_tx_valid = 1'b0;
|
||||
tx_fifo_clr_n = 1'b0; // self clearing
|
||||
rx_fifo_clr_n = 1'b0; // self clearing
|
||||
|
||||
// rx status
|
||||
regs_n[LSR][0] = fifo_rx_valid; // fifo is empty
|
||||
|
||||
// rx data discarded (no overrun in fact, the last rx byte was discarded.)
|
||||
regs_n[LSR][1] = rx_overrun;
|
||||
|
||||
// parity error on receiving part has occured
|
||||
regs_n[LSR][2] = parity_error;
|
||||
|
||||
// tx status register
|
||||
regs_n[LSR][5] = ~ (|tx_elements); // fifo is empty
|
||||
regs_n[LSR][6] = tx_ready & ~ (|tx_elements); // shift register and fifo are empty
|
||||
|
||||
if (PSEL && PENABLE && PWRITE)
|
||||
begin
|
||||
case (register_adr)
|
||||
|
||||
THR: // either THR or DLL
|
||||
begin
|
||||
if (regs_q[LCR][7]) // Divisor Latch Access Bit (DLAB)
|
||||
begin
|
||||
regs_n[DLL + 'd8] = PWDATA[7:0];
|
||||
end
|
||||
else
|
||||
begin
|
||||
fifo_tx_data = PWDATA[7:0];
|
||||
fifo_tx_valid = 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
IER: // either IER or DLM
|
||||
begin
|
||||
if (regs_q[LCR][7]) // Divisor Latch Access Bit (DLAB)
|
||||
regs_n[DLM + 'd8] = PWDATA[7:0];
|
||||
else
|
||||
regs_n[IER] = PWDATA[7:0];
|
||||
end
|
||||
|
||||
LCR:
|
||||
regs_n[LCR] = PWDATA[7:0];
|
||||
|
||||
FCR: // write only register, fifo control register
|
||||
begin
|
||||
rx_fifo_clr_n = PWDATA[1];
|
||||
tx_fifo_clr_n = PWDATA[2];
|
||||
trigger_level_n = PWDATA[7:6];
|
||||
end
|
||||
|
||||
default: ;
|
||||
endcase
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
// register read logic
|
||||
always_comb
|
||||
begin
|
||||
PRDATA = 'b0;
|
||||
apb_rx_ready = 1'b0;
|
||||
fifo_rx_ready = 1'b0;
|
||||
clr_int = 4'b0;
|
||||
|
||||
if (PSEL && PENABLE && !PWRITE)
|
||||
begin
|
||||
case (register_adr)
|
||||
RBR: // either RBR or DLL
|
||||
begin
|
||||
if (regs_q[LCR][7]) // Divisor Latch Access Bit (DLAB)
|
||||
PRDATA = {24'b0, regs_q[DLL + 'd8]};
|
||||
else
|
||||
begin
|
||||
|
||||
fifo_rx_ready = 1'b1;
|
||||
|
||||
PRDATA = {24'b0, fifo_rx_data[7:0]};
|
||||
|
||||
clr_int = 4'b1100; // clear Received Data Available interrupt
|
||||
end
|
||||
end
|
||||
|
||||
LSR: // Line Status Register
|
||||
begin
|
||||
PRDATA = {24'b0, regs_q[LSR]};
|
||||
clr_int = 4'b0110; // clear parrity interrupt error
|
||||
end
|
||||
|
||||
LCR: // Line Control Register
|
||||
PRDATA = {24'b0, regs_q[LCR]};
|
||||
|
||||
IER: // either IER or DLM
|
||||
begin
|
||||
if (regs_q[LCR][7]) // Divisor Latch Access Bit (DLAB)
|
||||
PRDATA = {24'b0, regs_q[DLM + 'd8]};
|
||||
else
|
||||
PRDATA = {24'b0, regs_q[IER]};
|
||||
end
|
||||
|
||||
IIR: // interrupt identification register read only
|
||||
begin
|
||||
PRDATA = {24'b0, 1'b1, 1'b1, 2'b0, IIR_o};
|
||||
clr_int = 4'b0010; // clear Transmitter Holding Register Empty
|
||||
end
|
||||
|
||||
default: ;
|
||||
endcase
|
||||
end
|
||||
end
|
||||
|
||||
// synchronouse part
|
||||
always_ff @(posedge CLK, negedge RSTN)
|
||||
begin
|
||||
if(~RSTN)
|
||||
begin
|
||||
|
||||
regs_q[IER] <= 8'h0;
|
||||
regs_q[IIR] <= 8'h1;
|
||||
regs_q[LCR] <= 8'h3;
|
||||
regs_q[MCR] <= 8'h0;
|
||||
regs_q[LSR] <= 8'h60;
|
||||
regs_q[MSR] <= 8'h0;
|
||||
regs_q[SCR] <= 8'h0;
|
||||
regs_q[DLM + 'd8] <= 8'd1; // 50e6/115200 = 434
|
||||
regs_q[DLL + 'd8] <= 8'd178;
|
||||
|
||||
trigger_level_q <= 2'b00;
|
||||
tx_fifo_clr_q <= 1'b0;
|
||||
rx_fifo_clr_q <= 1'b0;
|
||||
|
||||
end
|
||||
else
|
||||
begin
|
||||
regs_q <= regs_n;
|
||||
|
||||
trigger_level_q <= trigger_level_n;
|
||||
tx_fifo_clr_q <= tx_fifo_clr_n;
|
||||
rx_fifo_clr_q <= rx_fifo_clr_n;
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
assign register_adr = {PADDR[2:0]};
|
||||
// APB logic: we are always ready to capture the data into our regs
|
||||
// not supporting transfare failure
|
||||
assign PREADY = 1'b1;
|
||||
assign PSLVERR = 1'b0;
|
||||
|
||||
assign rda_o = regs_q[LSR][0]; // rx data avaiable
|
||||
assign tde_o = regs_q[LSR][5]; // tx data empty
|
||||
|
||||
endmodule
|
||||
162
new_project_rtl_template/sim/apb_uart/apb_uart_tx.sv
Normal file
162
new_project_rtl_template/sim/apb_uart/apb_uart_tx.sv
Normal file
@@ -0,0 +1,162 @@
|
||||
|
||||
module apb_uart_tx (
|
||||
input logic clk_i,
|
||||
input logic rstn_i,
|
||||
output logic tx_o,
|
||||
output logic busy_o,
|
||||
input logic cfg_en_i,
|
||||
input logic [15:0] cfg_div_i,
|
||||
input logic cfg_parity_en_i,
|
||||
input logic cfg_even_parity_i,
|
||||
input logic [1:0] cfg_bits_i,
|
||||
input logic cfg_stop_bits_i,
|
||||
input logic [7:0] tx_data_i,
|
||||
input logic tx_valid_i,
|
||||
output logic tx_ready_o
|
||||
);
|
||||
|
||||
enum logic [2:0] {IDLE,START_BIT,DATA,PARITY,STOP_BIT_FIRST,STOP_BIT_LAST} CS,NS;
|
||||
|
||||
logic [7:0] reg_data;
|
||||
|
||||
logic [2:0] reg_bit_count;
|
||||
|
||||
logic [2:0] s_target_bits;
|
||||
|
||||
logic parity_bit;
|
||||
|
||||
logic [15:0] baud_cnt;
|
||||
logic baudgen_en;
|
||||
logic bit_done;
|
||||
|
||||
assign busy_o = (CS != IDLE);
|
||||
|
||||
always_comb begin
|
||||
case(cfg_bits_i)
|
||||
2'b00:
|
||||
s_target_bits = 3'h4;
|
||||
2'b01:
|
||||
s_target_bits = 3'h5;
|
||||
2'b10:
|
||||
s_target_bits = 3'h6;
|
||||
2'b11:
|
||||
s_target_bits = 3'h7;
|
||||
endcase
|
||||
end
|
||||
|
||||
always_comb begin
|
||||
NS = CS;
|
||||
tx_o = 1'b1;
|
||||
tx_ready_o = 1'b0;
|
||||
baudgen_en = 1'b0;
|
||||
case(CS)
|
||||
IDLE: begin
|
||||
if (cfg_en_i)
|
||||
tx_ready_o = 1'b1;
|
||||
if (tx_valid_i) begin
|
||||
NS = START_BIT;
|
||||
end
|
||||
end
|
||||
|
||||
START_BIT: begin
|
||||
tx_o = 1'b0;
|
||||
baudgen_en = 1'b1;
|
||||
if (bit_done)
|
||||
NS = DATA;
|
||||
end
|
||||
|
||||
DATA: begin
|
||||
tx_o = reg_data[0];
|
||||
baudgen_en = 1'b1;
|
||||
if (bit_done) begin
|
||||
if (reg_bit_count == s_target_bits) begin
|
||||
if (cfg_parity_en_i) begin
|
||||
NS = PARITY;
|
||||
end else begin
|
||||
NS = STOP_BIT_FIRST;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
PARITY: begin
|
||||
tx_o = parity_bit;
|
||||
baudgen_en = 1'b1;
|
||||
if (bit_done)
|
||||
NS = STOP_BIT_FIRST;
|
||||
end
|
||||
STOP_BIT_FIRST: begin
|
||||
tx_o = 1'b1;
|
||||
baudgen_en = 1'b1;
|
||||
if (bit_done) begin
|
||||
if (cfg_stop_bits_i)
|
||||
NS = STOP_BIT_LAST;
|
||||
else
|
||||
NS = IDLE;
|
||||
end
|
||||
end
|
||||
STOP_BIT_LAST: begin
|
||||
tx_o = 1'b1;
|
||||
baudgen_en = 1'b1;
|
||||
if (bit_done) begin
|
||||
NS = IDLE;
|
||||
end
|
||||
end
|
||||
default:
|
||||
NS = IDLE;
|
||||
endcase
|
||||
end
|
||||
|
||||
always_ff @(posedge clk_i or negedge rstn_i) begin
|
||||
if (rstn_i == 1'b0) begin
|
||||
CS <= IDLE;
|
||||
reg_data <= 8'hFF;
|
||||
reg_bit_count <= 'h0;
|
||||
parity_bit <= 1'b0;
|
||||
end else begin
|
||||
if(cfg_en_i)
|
||||
CS <= NS;
|
||||
else
|
||||
CS <= IDLE;
|
||||
|
||||
case (CS)
|
||||
IDLE:
|
||||
if (tx_valid_i)
|
||||
reg_data <= tx_data_i;
|
||||
START_BIT:
|
||||
parity_bit <= ~cfg_even_parity_i;
|
||||
DATA:
|
||||
if (bit_done) begin
|
||||
parity_bit <= parity_bit ^ reg_data[0];
|
||||
if (reg_bit_count == s_target_bits)
|
||||
reg_bit_count <= 'h0;
|
||||
else begin
|
||||
reg_bit_count <= reg_bit_count + 1;
|
||||
reg_data <= {1'b1,reg_data[7:1]};
|
||||
end
|
||||
end
|
||||
endcase
|
||||
end
|
||||
end
|
||||
|
||||
always_ff @(posedge clk_i or negedge rstn_i) begin
|
||||
if (rstn_i == 1'b0) begin
|
||||
baud_cnt <= 'h0;
|
||||
bit_done <= 1'b0;
|
||||
end else begin
|
||||
if(baudgen_en) begin
|
||||
if(baud_cnt == cfg_div_i) begin
|
||||
baud_cnt <= 'h0;
|
||||
bit_done <= 1'b1;
|
||||
end else begin
|
||||
baud_cnt <= baud_cnt + 1;
|
||||
bit_done <= 1'b0;
|
||||
end
|
||||
end else begin
|
||||
baud_cnt <= 'h0;
|
||||
bit_done <= 1'b0;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
BIN
new_project_rtl_template/sim/apb_uart/doc/uart_registers.xlsx
Normal file
BIN
new_project_rtl_template/sim/apb_uart/doc/uart_registers.xlsx
Normal file
Binary file not shown.
139
new_project_rtl_template/sim/apb_uart/io_generic_fifo.sv
Normal file
139
new_project_rtl_template/sim/apb_uart/io_generic_fifo.sv
Normal file
@@ -0,0 +1,139 @@
|
||||
// Copyright 2017 ETH Zurich and University of Bologna.
|
||||
// Copyright and related rights are licensed under the Solderpad Hardware
|
||||
// License, Version 0.51 (the “License”); you may not use this file except in
|
||||
// compliance with the License. You may obtain a copy of the License at
|
||||
// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
|
||||
// or agreed to in writing, software, hardware and materials distributed under
|
||||
// this 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.
|
||||
|
||||
module io_generic_fifo
|
||||
#(
|
||||
parameter DATA_WIDTH = 32,
|
||||
parameter BUFFER_DEPTH = 2,
|
||||
parameter LOG_BUFFER_DEPTH = $clog2(BUFFER_DEPTH)
|
||||
)
|
||||
(
|
||||
input logic clk_i,
|
||||
input logic rstn_i,
|
||||
|
||||
input logic clr_i,
|
||||
|
||||
output logic [LOG_BUFFER_DEPTH:0] elements_o,
|
||||
|
||||
output logic [DATA_WIDTH-1 : 0] data_o,
|
||||
output logic valid_o,
|
||||
input logic ready_i,
|
||||
|
||||
input logic valid_i,
|
||||
input logic [DATA_WIDTH-1 : 0] data_i,
|
||||
output logic ready_o
|
||||
);
|
||||
|
||||
// Internal data structures
|
||||
/* verilator lint_off WIDTH */
|
||||
logic [LOG_BUFFER_DEPTH-1:0] pointer_in; // location to which we last wrote
|
||||
logic [LOG_BUFFER_DEPTH-1:0] pointer_out; // location from which we last sent
|
||||
/* lint_off */
|
||||
logic [LOG_BUFFER_DEPTH:0] elements; // number of elements in the buffer
|
||||
logic [DATA_WIDTH-1:0] buffer [BUFFER_DEPTH - 1 : 0];
|
||||
|
||||
logic full;
|
||||
|
||||
int unsigned loop1;
|
||||
|
||||
assign full = (elements == BUFFER_DEPTH);
|
||||
assign elements_o = elements;
|
||||
|
||||
always_ff @(posedge clk_i, negedge rstn_i)
|
||||
begin: elements_sequential
|
||||
if (rstn_i == 1'b0)
|
||||
elements <= 0;
|
||||
else
|
||||
begin
|
||||
if (clr_i)
|
||||
elements <= 0;
|
||||
else
|
||||
begin
|
||||
// ------------------
|
||||
// Are we filling up?
|
||||
// ------------------
|
||||
// One out, none in
|
||||
if (ready_i && valid_o && (!valid_i || full))
|
||||
elements <= elements - 1;
|
||||
// None out, one in
|
||||
else if ((!valid_o || !ready_i) && valid_i && !full)
|
||||
elements <= elements + 1;
|
||||
// Else, either one out and one in, or none out and none in - stays unchanged
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
always_ff @(posedge clk_i, negedge rstn_i)
|
||||
begin: buffers_sequential
|
||||
if (rstn_i == 1'b0)
|
||||
begin
|
||||
for (loop1 = 0 ; loop1 < BUFFER_DEPTH ; loop1 = loop1 + 1)
|
||||
buffer[loop1] <= 0;
|
||||
end
|
||||
else
|
||||
begin
|
||||
// Update the memory
|
||||
if (valid_i && !full)
|
||||
buffer[pointer_in] <= data_i;
|
||||
end
|
||||
end
|
||||
|
||||
always_ff @(posedge clk_i, negedge rstn_i)
|
||||
begin: sequential
|
||||
if (rstn_i == 1'b0)
|
||||
begin
|
||||
pointer_out <= 0;
|
||||
pointer_in <= 0;
|
||||
end
|
||||
else
|
||||
begin
|
||||
if(clr_i)
|
||||
begin
|
||||
pointer_out <= 0;
|
||||
pointer_in <= 0;
|
||||
end
|
||||
else
|
||||
begin
|
||||
// ------------------------------------
|
||||
// Check what to do with the input side
|
||||
// ------------------------------------
|
||||
// We have some input, increase by 1 the input pointer
|
||||
if (valid_i && !full)
|
||||
begin
|
||||
if (pointer_in == $unsigned(BUFFER_DEPTH - 1))
|
||||
pointer_in <= 0;
|
||||
else
|
||||
pointer_in <= pointer_in + 1;
|
||||
end
|
||||
// Else we don't have any input, the input pointer stays the same
|
||||
|
||||
// -------------------------------------
|
||||
// Check what to do with the output side
|
||||
// -------------------------------------
|
||||
// We had pushed one flit out, we can try to go for the next one
|
||||
if (ready_i && valid_o)
|
||||
begin
|
||||
if (pointer_out == $unsigned(BUFFER_DEPTH - 1))
|
||||
pointer_out <= 0;
|
||||
else
|
||||
pointer_out <= pointer_out + 1;
|
||||
end
|
||||
// Else stay on the same output location
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// Update output ports
|
||||
assign data_o = buffer[pointer_out];
|
||||
assign valid_o = (elements != 0);
|
||||
|
||||
assign ready_o = ~full;
|
||||
|
||||
endmodule
|
||||
Reference in New Issue
Block a user