串并转换
串并转换一般用来各种低速通信协议中,比如UART, SPI, I2C等。
UART的传输速率有9600,38400,191200等,K级别,CPU频率是M级别,串并转换可以作为数据缓冲模块,提高数据传输效率
wire data_out;
reg [7:0] temp;
always @(posedge clk or negedge rstn) begin
if(!rstn)
temp <= 8'h0;
else if(load)
temp <= data_in;
else if(en)
temp <= {temp[6:0], 1'b0};
else
temp <= temp;
end
assign data_out = temp[7];
// 并转串,比如CPU给uart发送串行数据,1bit,1bit发送
input wire data_in;
temp <= {temp[6:0], data_in};
// 串转并
FIFO&FILO
FIFO通常用在通信双方数据传输速率不匹配的情况下,硬件实现。
FILO与软件中的堆栈类型,完成入栈和出栈操作,一般采用软件方式实现。
FIFO分为同步fifo和异步fifo,异步fifo指的是读写时钟域的速率不同,当读写时钟速率差越大,所需fifo的深度越小,反之,深度越大。同步fifo指的是读写时钟速率相同,仅仅完成的是数据搬移工作,比如DMA的工作,在DMA控制器内部有一个同步的fifo作为数据缓冲,可以提高数据传输的速率。
此外,为了设计方便,FIFO的输入和输出位宽一般是相同的,如果不相同,要通过上文提到的串并转换方法进行位宽的调整。
- 同步fifo_demo
module fifo_sync #(
parameter DATA_WIDTH = 32,
parameter RAM_DEPTH = 8,
parameter RAM_ADDR = 3
)(
input clk,
input rstn,
input wr_en,
input [DATA_WIDTH-1:0] wr_data,
input rd_en,
output reg [DATA_WIDTH-1:0] rd_data,
output reg fifo_full,
output reg fifo_empyt
);
reg [DATA_WIDTH-1:0] ram[RAM_DEPTH];
reg [RAM_ADDR-1:0] wr_ptr, rd_ptr;
reg [RAM_ADDR-1:0] counter;
integer i;
always @(posedge clk or negedge rstn) begin
if(!rstn) begin
for(i=0;i<RAM_DEPTH)
ram[i] <= 0;
end
else if(!fifo_full & wr_en)
ram[wr_ptr] <= wr_data;
end
always @(*) begin // comb logic
if(!rstn) begin
rd_data = 0;
end
else if(rd_en & !fifo_empyt)
rd_data = ram[rd_ptr];
else
rd_data = 0;
end
always @(posedge clk or negedge rstn) begin
if(!rstn) begin
wr_ptr <= 0;
end
else if(wr_en & ~fifo_full)
wr_ptr <= wr_ptr + 1'b1;
end
always @(posedge clk or negedge rstn) begin
if(!rstn) begin
rd_ptr <= 0;
end
else if(rd_en & ~fifo_empyt)
rd_ptr <= rd_ptr + 1'b1;
end
always @(posedge clk or negedge rstn) begin
if(!rstn) begin
counter <= 'h0;
end
else if(wr_ptr && !fifo_full)
counter <= counter + 1'b1;
else if(rd_ptr && !fifo_empyt)
counter <= counter - 1'b1;
end
always @(*)
if(counter == RAM_DEPTH-1)
fifo_full = 1;
else
fifo_full = 0;
always @(*)
if(counter == 0)
fifo_empyt = 1;
else
fifo_empyt = 0;
endmodule
- 异步fifo的demo
/*********************************************************************
* Author : liu
* Last modified : 2023-04-11 22:24
* Filename : asyn_fifo.v
* Description :
* ******************************************************************/
module asyn_fifo(
input rstn,
input wr_clk,
input [31:0] wr_data,
input wr_en,
input rd_clk,
input rd_en,
output [31:0] rd_data,
output fifo_full,
output fifo_empty
);
// generate meomory
// 32 * 16bit
// depath : 32
// width : 16
reg [31:0] memory[0:15];
// wr&rd ptr gray
reg [4:0] wr_ptr;
reg [4:0] wr_ptr_gray;
reg [4:0] wr_ptr_gray_r;
reg [4:0] wr_ptr_gray_rr;
wire [4:0] wr_ptr_bin2gray;
reg [4:0] rd_ptr;
reg [4:0] rd_ptr_gray;
reg [4:0] rd_ptr_gray_r;
reg [4:0] rd_ptr_gray_rr;
wire [4:0] rd_ptr_bin2gray;
integer i;
always @(posedge wr_clk or negedge rstn) begin
if(!rstn) begin
for(i=0;i<15;i=i+1)
memory[i] <= 32'h0;
end else if(wr_en && !fifo_full) begin
memory[wr_ptr] <= wr_data;
end
end
// read domain
always @(posedge rd_clk or negedge rstn) begin
if(!rstn)
rd_ptr <= 5'h0;
else if(rd_en && !fifo_empty)
rd_ptr <= rd_ptr + 1'b1;
end
// reg to write clk domain
assign rd_ptr_bin2gray = (rd_ptr>>1)^(rd_ptr);
always @(posedge rd_clk or negedge rstn) begin
if(!rstn)
rd_ptr_gray <= 0;
else
rd_ptr_gray <= rd_ptr_bin2gray;
end
assign rd_data = memory[rd_ptr[3:0]];
always @(posedge rd_clk or negedge rstn) begin
if(!rstn) begin
wr_ptr_gray_r <= wr_ptr_gray;
wr_ptr_gray_rr <= wr_ptr_gray_r;
end else begin
wr_ptr_gray_r <= wr_ptr_gray;
wr_ptr_gray_rr <= wr_ptr_gray_r;
end
end
// write domain
always @(posedge wr_clk or negedge rstn) begin
if(!rstn) begin
wr_ptr <= 5'h0;
end
else if(wr_en && !fifo_full) begin
wr_ptr <= wr_ptr + 1'b1;
end
end
assign wr_ptr_bin2gray = (wr_ptr>>1)^(wr_ptr);
// reg to read clk domain
always @(posedge wr_clk or negedge rstn) begin
if(!rstn)
wr_ptr_gray <= 0;
else
wr_ptr_gray <= wr_ptr_bin2gray;
end
always @(posedge wr_clk or negedge rstn) begin
if(!rstn) begin
rd_ptr_gray_r <= rd_ptr_gray;
rd_ptr_gray_rr <= rd_ptr_gray_r;
end else begin
rd_ptr_gray_r <= rd_ptr_gray;
rd_ptr_gray_rr <= rd_ptr_gray_r;
end
end
assign fifo_empty = wr_ptr_gray_rr == rd_ptr_gray;
assign fifo_full = (rd_ptr_gray_rr[4] != wr_ptr_gray[4])&&(rd_ptr_gray_rr[3] != wr_ptr_gray[3])&&(rd_ptr_gray_rr[2:0] == wr_ptr_gray[2:0]);
endmodule
watchdog(硬件实现)
满足第一个预置数,向CPU发起中断,请求对watchdog计数清零,如果等到预置数1+预置数2还没有等到清零信号,说明系统跑飞了,则发起全局复位信号,对系统进行复位。
module wtd(
input clk,
input rstn,
input clear,
input [7:0] preload1,
input [7:0] preload2,
output reg done,
output reg reset
);
reg [7:0] cnt;
always @(posedge clk or negedge rstn) begin
if(!rstn)
cnt <= 8'h0;
else if(clear)
cnt <= 8'h0;
else if(cnt <= preload1 + preload2)
cnt <= cnt + 8'h1;
else
cnt <= cnt;
end
always @(posedge clk or negedge rstn) begin
if(!rstn)
done <= 1'b0;
else if(clear)
done <= 1'b0;
else if(cnt == preload1)
done <= 1'b1;
end
always @(posedge clk or negedge rstn) begin
if(!rstn)
reset <= 1'b0;
else if(cnt == preload1 + preload2)
reset <= 1'b1;
end
endmodule
对于硬件寄存器的访问,有两种方式:
- 硬件直接访问
- 通过软件方式间接访问