本章节主要使用ddr3做为缓存,串口接收的数据通过ddr缓存后通过发送模块发送出去。整体的功能框图所下图所示
写通道
串口接收到8位数据后,将4个8位数据合并为一个32位数据写入到写fifo,当写入8个32位数据后,也就是一共256位宽数据,这时会发出一个突发写使能信号wr_len_en,将256位数据写入到ddr3中存储(rd_data_count = 9'd1表示写fifo已经写入一个256位宽数据)
//突发写使能
always@(posedge ui_clk or negedge i_rst_n)begin
if(!i_rst_n)
wr_len_en <= 1'd0;
else if(ddr_wr_end)
wr_len_en <= 1'd0;
else if(rd_len_en)
wr_len_en <= 1'd0;
else if(wr_len_done)
wr_len_en <= 1'd0;
else if(rd_data_count >= 9'd1)
wr_len_en <= 1'd1;
end
读通道
当写fifo写入256数据后,并且突发写完成(wr_len_done为突发写完成信号),这时会产生一个突发读使能信号rd_len_en,将ddr3中的256位宽数据读出写入到读fifo中
always@(posedge ui_clk or negedge i_rst_n)begin
if(!i_rst_n)
wr_len_done_r <= 1'b0;
else
wr_len_done_r <= wr_len_done;
end
//突发读使能
always@(posedge ui_clk or negedge i_rst_n)begin
if(!i_rst_n)
rd_len_en <= 1'd0;
else if(ddr_rd_end)
rd_len_en <= 1'd0;
else if(wr_len_en)
rd_len_en <= 1'd0;
else if(rd_len_done)
rd_len_en <= 1'd0;
else if(wr_len_done_r)
rd_len_en <= 1'd1;
end
当tx_data_convert模块检测读fifo的read_fifo_empty为0时,也就是读fifo有数据时,会产生一个read_en读使能信号,读出读fifo的数据,每次都是读一个32位数据read_dout
always@(posedge i_clk or negedge i_rst_n)begin
if(!i_rst_n)
read_data <= 32'd0;
else if(count == 4 )
read_data <= 32'd0;
else if(read_en)
read_data <= read_dout;
else
read_data <= read_data;
end
always@(posedge i_clk or negedge i_rst_n)begin
if(!i_rst_n)begin
read_en <= 1'd0;
read_en_flag <= 1'd0;
end
else if(count == 4 )begin
read_en <= 1'd0;
read_en_flag <= 1'd0;
end
else if(read_en_flag)
read_en <= 1'd0;
else if(!read_fifo_empty)begin
read_en <= 1'd1;
read_en_flag <= 1'd1;
end
else
read_en <= 1'd0;
end
最后tx_data_conver模块会将读出32位数据转换为4个8位数据,并且通过uart_tx发送出去
always@(posedge i_clk or negedge i_rst_n)begin
if(!i_rst_n)
tx_data <= 8'd0;
else case(count)
0: tx_data <= read_data[31:24];
1: tx_data <= read_data[23:16];
2: tx_data <= read_data[15:8];
3: tx_data <= read_data[7:0];
default:tx_data <= tx_data;
endcase
end
uart_tx uart_tx_inst(
. sclk (clk_50M),
. s_rst_n (rst_n),
// UART Interface
. rs232_tx (rs232_tx),
. tx_flag (tx_flag),
. tx_done (tx_done),
// others
. tx_trig (tx_trig),//tx_trig
. tx_data (tx_data)//tx_data
)
将开发板上电,连接串口
因ddr3突发一次是256位宽,也就是32个8位数据,所以一次发32个8位数据开发板接收后才会将接收到的数据发送出来。
串口未发送任何数据
串口发送32个8位数据,可以看到发送的和接收的数据是一致的
点重复发送,看下效果,发送和接收的数据一致并且数量一样
重复发送后,可以看到,当数据接收到2048就停止了
为什么接收到2048就停止了,打开程序发现我们设置ddr最大的读写地址为512,每次都是突发8个32位256位宽数据,设置为512表示一共计数512个32位宽的数据,换算一下就是2048个8位数据刚好和接收停止的一致
所以我们这里需要对ddr的读写地址进行清零,才能再次接收,这里使用ddr读写完成信号ddr_wr_end和ddr_rd_end产生vin_vs和vout_vs信号,发出wr_reset和rd_reset对地址进行清零
always@(posedge ui_clk or negedge rst_n)begin
if(!rst_n)
wr_load_r <= 1'd0;
else
wr_load_r <= {wr_load_r[14:0],vin_vs};
end
always @(posedge ui_clk or negedge rst_n) begin
if(!rst_n)
wr_page <= 3'd0;
//else if(wr_load_r[0] && !wr_load_r[14])
//wr_page <= wr_page + 1'b1;
end
always@(posedge ui_clk or negedge rst_n)begin
if(!rst_n)
wr_reset <= 1'd0;
else if(wr_load_r[0] && !wr_load_r[14])
wr_reset <= 1'd1;
else if(app_addr_wr == 0 && !(wr_load_r[0] && !wr_load_r[14]))
wr_reset <= 1'd0;
end
always@(posedge ui_clk or negedge rst_n)begin
if(!rst_n)
rd_load_r <= 1'd0;
else
rd_load_r <= {rd_load_r[14:0],vout_vs};
end
always @(posedge ui_clk or negedge rst_n) begin
if(!rst_n)
rd_page <= 3'd0;
//else if(rd_load_r[0] && !rd_load_r[14])
//rd_page <= wr_page - 1'b1;
end
always@(posedge ui_clk or negedge rst_n)begin
if(!rst_n)
rd_reset <= 1'd0;
else if(rd_load_r[0] && !rd_load_r[14])
rd_reset <= 1'd1;
else if(app_addr_rd == 0 && !(rd_load_r[0] && !rd_load_r[14]))
rd_reset <= 1'd0;
end
重新编译工程,将程序下载开发板,重复发送,可以看到发送和接收都是一致的
整个工程文件较多,所以不贴代码了
ddr3的简单用法就写到这里,如果有不懂的可以直接下载工程进行仿真测试
链接:https://pan.baidu.com/s/1TOrTU7i1RpgEXw-igxiL1A
提取码:2x12
ddr读写需要大家理解读写突发的基本概念,如果基本的读写突发能够理解,基本这个工程也能完全的看懂,这个uart工程每次只进行一次突发传输,所以还是比较好理解的
从评论可以看到64个32位宽数据和128个32位宽数据出现问题,通过仿真发现如果设置为64个32位数据也就是两个突发长度确实会出现问题
2次突发更改的地方
仿真到1个突发长度
rd_data_count计数器确实是1
第二个突发长度
rd_data_count计数器还是1,并不是我们期望的2,所以无法产生突发写命令
从官方的fifo仿真波形来,前面三次写的数据都会出现rd_data_count计数器保持为1的情况
直到第四fifo读写数据才开始是正常的计数相加
将上面总结一下,按照两次突发,可以看出fifo计数rd_data_count前三次都是值为1保持不变,只在第四次开始正常的相加,也就是第四次才能产生一个rd_data_count等于2,并且产生一次写突发,既然四次才能产生一次写突发并且同时产生读突发,那么我们直接一次写入四个突发长度,也就是4个256位数据,一共128个8位数据,因四个突发长度只能产生一次写突发,所以只能打印出前面2个突发长度的数据
从上图可以看出发送了4个突发长度数据,按照程序来看确实打印了64个8位数据,也就是一次2个突发长度的数据,还有第二次的2个突发长度数据没有出来,也就是41~80这部分数据没有出来,因fifo后面的数据突发都是正常的,所以我们再发送一次2个突发长度数据,将41~80这部分数据给挤出来,为了区别,这次发送的2个突发长度都是设置为一样的数据
按照程序成功的将第二次的fifo里面的数据挤出来了,正常打印了41~80的数据,我们再次发送2个突发长度的数据将将这次发送相同的01数据都给打印出来
从上面可以看出来数据是连续的并且是完整的,对于ddr这个fifo虽然前面几次突发有这样的问题,但整体的数据完整性是没有问题,再发送依然是将上次的挤出来了
那有没有办法可以解决这样的两次突发都正常,可以使用写fifo中的wr_data_count做为计数
下面的截图执行了两次突发,可以看到每次突发都是wr_data_count都是0~8,可以每次等于8就计数器加一,执行两次突发会出现两个8也就相当于计数为2,可以直接触发突发写,这样可以避免出现rd_data_count前面三次突发出现一直保持为1的情况,当然如果是摄像头类的大量数据还是建议使用rd_data_count这种方式,这种方法大家可以尝试一下
因2次突发读写和4次突发读写是一样的,所以这里不再继续测试4次突发读写,大家有兴趣可以尝试一下
因有人在下面评论串口接收后,发送会不会速度跟不上,所以将工程又改了一下
突发长度不变
突发写使能不变
//突发写使能
always@(posedge ui_clk or negedge i_rst_n)begin
if(!i_rst_n)
wr_len_en <= 1'd0;
else if(ddr_wr_end)
wr_len_en <= 1'd0;
else if(rd_len_en)
wr_len_en <= 1'd0;
else if(wr_len_done)
wr_len_en <= 1'd0;
else if(rd_data_count >= 9'd1)
wr_len_en <= 1'd1;
end
突发读变为突发写结束后,才开始进行突发读并且通过串口打印输出,也就是接收完成所有数据后,再通过串口打印输出。其中ddr_wr_end为ddr写完成信号,ddr的最大地址设置为256,说明一共传输256个32位数据,也就是1024个8位就会拉高ddr_wr_end信号,并且会触发ddr读突发,将写入到ddr里面的数据通过串口都打印出来
//突发读使能
always@(posedge ui_clk or negedge i_rst_n)begin
if(!i_rst_n)
rd_len_en <= 1'd0;
else if(ddr_rd_end)
rd_len_en <= 1'd0;
else if(wr_len_en)
rd_len_en <= 1'd0;
else if(rd_len_done)
rd_len_en <= 1'd0;
else if(ddr_wr_end && wr_data_count <= 9'd1)
rd_len_en <= 1'd1;
end
串口准备1024个8位数据
发送数据,发送和接收都是正常的,并且数据量是一样的
从ila可以看到,wr_len_en为写突发最后一次突发传输,传输完成后立马产生ddr_wr_en写完成信号,并且产生了突发读使能rd_len_en一共进行了8次突发读
同样再次测试也可以抓到最后一次突发读rd_len_en,并且产生ddr_rd_end结束信号
工程链接:https://pan.baidu.com/s/1PWE1cw9OlI9Zgvz-kj6MDw 文章来源:https://www.toymoban.com/news/detail-415339.html
提取码:czez文章来源地址https://www.toymoban.com/news/detail-415339.html
到了这里,关于AX7A200教程(5): 基于DDR3的串口发送和接收的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!