在设计和验证 FPGA 设计时,硬件描述语言 (HDL) 的使用正成为越来越重要的因素。行为级描述的使用不仅提高了设计生产力,而且在设计验证中提供了独特的优势。当今最主要的 HDL 称为 Verilog 和 VHDL。本应用说明 Verilog 在数字 UART(通用异步接收器和发送器)的设计和验证中的使用。
UART 由两个独立的 HDL 模块组成。一个模块实现发送器,而另一个模块实现接收器。发射器和接收器模块可以在设计的顶层进行组合,以实现所需的发射器和接收器通道的任意组合。数据可写入发送器并从接收器读出,全部通过单个 8 位双向 CPU 接口完成。发送器和接收器通道的地址映射可以轻松构建到设计顶层的接口中。两个模块共享一个名为 mclkx16 的公共主时钟。在每个模块内,mclkx16 被划分为独立的波特率时钟。
以下为相关Verilog代码
rxcver.v文件
module rxcver (mclkx16, read, rx, reset, rxrdy, parityerr, framingerr, overrun, data);
input mclkx16;
input read;
input rx;
input reset;
output rxrdy;
output parityerr; reg parityerr;
output framingerr; reg framingerr;
output overrun; reg overrun;
output [7:0] data; reg [7:0]data;
reg [3:0] rxcnt;
reg rx1, read1, read2, idle1, hunt;
reg [7:0] rhr;
reg [7:0] rsr;
reg rxparity;
reg paritygen;
reg rxstop;
reg rxclk;
reg idle;
reg rxdatardy;
always @(posedge rxclk or posedge reset)
begin
if (reset)
idle <= 1'b1;
else
idle <= !idle && !rsr[0];
end
always @(posedge mclkx16)
begin
if (reset)
hunt <= 1'b0;
else if (idle && !rx && rx1 )
hunt <= 1'b1;
else if (!idle || rx )
hunt <= 1'b0;
if (!idle || hunt)
rxcnt <= rxcnt + 1;
else
rxcnt <= 4'b0001;
rx1 <= rx;
rxclk <= rxcnt[3];
end
task idle_reset;
begin
rsr <= 8'b11111111;
rxparity <= 1'b1;
paritygen <= 1'b1;
rxstop <= 1'b0;
end
endtask
task shift_data;
begin
rsr <= rsr >> 1;
rsr[7] <= rxparity;
rxparity <= rxstop;
rxstop <= rx;
paritygen <= paritygen ^ rxstop;
end
endtask
always @(posedge rxclk or posedge reset)
if (reset)
idle_reset;
else
begin
if (idle)
idle_reset;
else
shift_data;
end
always @(posedge mclkx16 or posedge reset)
if (reset)
begin
rhr <= 8'h00;
rxdatardy <= 1'b0;
overrun <= 1'b0;
parityerr <= 1'b0;
framingerr <= 1'b0;
idle1 <= 1'b1;
read2 <= 1'b1;
read1 <= 1'b1;
end
else
begin
if (idle && !idle1)
begin
if (rxdatardy)
overrun <= 1'b1;
else
begin
overrun <= 1'b0;
rhr <= rsr;
parityerr <= paritygen;
framingerr <= !rxstop;
rxdatardy <= 1'b1;
end
end
if (!read2 && read1)
begin
rxdatardy <= 1'b0;
parityerr <= 1'b0;
framingerr <= 1'b0;
overrun <= 1'b0;
end
idle1 <= idle;
read2 <= read1;
read1 <= read;
end
assign rxrdy = rxdatardy;
always @(read or rhr)
if (~read)
data = rhr;
endmodule
txmit.v文件
module txmit (mclkx16, write, reset, tx, txrdy, data);
input mclkx16; // Input clock, 16 x baudrate clock used for synchronization.
input write; // Transmit write strobe.
input reset; // Global reset.
output tx; reg tx; // Transmit data output.
output txrdy; // Transmitter ready to recieve next byte to be send
input [7:0] data; // 8 bit input data bus
reg write1, write2; // write signal delayed 1 and 2 cycles.
reg txdone1; // txdone signal delayed one cycle.
// Transmit shift register bits
reg [7:0] thr; // Transmit hold register
reg [7:0] tsr; // Transmit shift register, used for shifting out data to tx.
reg tag1, tag2; // Tag bits for used for detecting, when the tsr are empty.
wire paritymode = 1'b1; // Initializing to 1 = odd parity, 0 = even parity.
reg txparity; // Parity generation register.
// Transmit clock and other control signals
reg txclk; // Transmit clock, i.e. baudrate clock = 1/16'th of mclkx16.
wire txdone; // Set to high, when shifting of byte is done.
wire paritycycle; // Set to high, one cycle next to last shift cycle.
reg txdatardy; // Set to hign, when data is ready in transmit hold register.
reg [2:0] cnt; // Counter used for generating the internal baud rate clock.
// Paritycycle = 1 on next to last cycle, this means when tsr[1] gets tag2.
assign paritycycle = tsr[1] && !(tag2 || tag1 || tsr[7] || tsr[6] || tsr[5] || tsr[4] || tsr[3] || tsr[2]);
// txdone = 1 when done shifting, this means when tx gets tag2.
assign txdone = !(tag2 || tag1 || tsr[7] || tsr[6] || tsr[5] || tsr[4] || tsr[3] || tsr[2] || tsr[1] || tsr[0]);
// Ready for new date to be written, when no data is in transmit hold register.
assign txrdy = !txdatardy;
// Latch data[7:0] into the transmit hold register at posedge of write.
always @(write or data)
if (~write)
thr = data;
// Toggle txclk every 8 counts, which divides the clock by 16, to generate the baud clock
always @(posedge mclkx16 or posedge reset)
if (reset)
begin
txclk <= 1'b0;
cnt <= 3'b000;
end
else
begin
if (cnt == 3'b000)
txclk <= !txclk;
cnt <= cnt + 1;
end
task idle_reset;
begin // Apply pseudo Idle state, equal to end of transmission.
tsr <= 8'h00; // Reset transmit shift register.
tag2 <= 1'b0; // Reset tag bit.
tag1 <= 1'b0; // Reset tag bit.
// txparity <= paritymode; // Set parity mode for even or odd parity.
txparity <= 1'b0; // Reset txparty bit.
tx <= 1'b1; // At pseudo idle set Start bit high.
end
endtask
task load_data;
begin // Initialize registers and load next byte of data.
tsr <= thr; // Load tsr from thr.
tag2 <= 1'b1; // Set tag bits for detecting when shifting is done.
tag1 <= 1'b1; // Set tag bits for detecting when shifting is done.
txparity <= paritymode; // Set parity mode bit, 0 = even parity, 1 = odd parity
tx <= 1'b0; // Set start bit low.
end
endtask
task shift_data;
begin
tsr <= tsr >> 1; // Right shift tsr by one.
tsr[7] <= tag1; // Set tsr[7] = tag1.
tag1 <= tag2; // Set tag1 = tag2.
tag2 <= 1'b0; // Set tag2 = 0.
txparity <= txparity ^ tsr[0]; // Generate parity.
end
endtask
// Shifting out data to tx.
always @(posedge txclk or posedge reset)
if (reset)
idle_reset; // Reset internal bits of transmitter.
else
begin
if (txdone && txdatardy)
load_data; // Load new data to tsr.
else
begin
shift_data; // Shift contents of tsr to tx output.
// Shift out data or parity bit or stop/idle bit.
if (txdone )
tx <= 1'b1; // Output stop/idle bit.
else if (paritycycle)
tx <= txparity; // Output parity bit.
else
tx <= tsr[0]; //Shift out data bit.
end
end
always @(posedge mclkx16 or posedge reset)
if (reset)
begin
txdatardy <= 1'b0;
write2 <= 1'b1;
write1 <= 1'b1;
txdone1 <= 1'b1; // Set equal to txdone at reset.
end
else
begin
if (write1 && !write2)
txdatardy <= 1'b1; // At risign edge of write, new data are latched in thr, and txdatardy are set.
else if (!txdone && txdone1)
txdatardy <= 1'b0; // At falling edge of txdone, the thr has been loaded into tsr, so txdatardy are reset.
// Generate delayed versions of write and txdone signals for edge detection.
write2 <= write1;
write1 <= write;
txdone1 <= txdone;
end
endmodule
uart.v文件
//Includes Verilog HDL sources for Transmitter & receiver modules
`include "txmit.v"
`include "rxcver.v"
module uart (mclkx16, reset, read, write, data, rx, tx, rxrdy, txrdy, parityerr, framingerr, overrun);
input mclkx16; // Input clock, 16 x baud rate clock used for synchronisation.
input read; // read strobe input.
input write; // write strobe input.
input reset; // Master reset input.
inout [7:0] data; // Bidirectional data bus for writing to transmitter & reading from receiver.
// Receiver input signal, error and status flags.
input rx; // Receive data line input
output rxrdy; wire rxrdy; // Data ready to be read.
output parityerr; wire parityerr; // Parity error flag.
output framingerr; wire framingerr; // Framing error flag.
output overrun; wire overrun; // Overrun error flag.
wire [7:0] rxdata; // Intermediate output signals from receiver.
// Transmitter output signal and status flag.
output tx; wire tx; // Transmit data line output
output txrdy; wire txrdy; // Transmitter ready for next byte.
//Instantiation of the transmitter module.
txmit tx_1 (mclkx16, write, reset, tx, txrdy, data);
// Instantiation of the receiver module.
rxcver rx_1 (mclkx16, read, rx, reset, rxrdy, parityerr, framingerr, overrun, rxdata);
//Drives the databus during data read, otherwise tri-state the data bus.
assign data = !read ? rxdata : 8'bzzzzzzzz;
endmodule