本博文设计思想采用明德扬至简设计法。之前都是通过一些完整的案例来分享设计心得,而这篇文章以需要配置多个寄存器的场景讲述核心设计技巧。
在设计案例时发现,经常会配置比较复杂的IP核或驱动一些接口进而操作外设。此时,为了让外设或IP核正常工作,需要对其内部多个寄存器进行适当配置来保证在所需模式下正常工作。我们一般先设计接口模块或IP核顶层文件,之后通过控制模块按照先后顺序自动给出所需指令,如读写等(下面的讲述以只有读写指令为例)。接口模块或IP核顶层模块收到指令后完成相应的操作。
第一个问题:如何实现多个寄存器且每个寄存器多个指令的自动化配置?
我们可以在控制模块中建立一个“配置表”,把读写指令以及相应的地址和待写入数据保存其中,然后通过计数器进行指令扫描。这里需要两级计数器,第一级计数一个寄存器的指令数,第二级计数器记录已经操作过的寄存器个数。配置表以always组合逻辑中case语句块形式给出,使用寄存器计数值区分不同寄存器。区分出待操作寄存器后根据操作计数器解析出读写指令。
第二个问题:当控制模块给出指令时,接口模块或IP核一定能有时间响应么?
这是我们设计时需要深思熟虑的问题:如何才能保证给出的指令一定会被下一模块有效地响应?为了实现这一目的,可以在控制(配置)模块和时序接口模块或IP核顶层模块之间放置一个接口衔接模块,结构如下:
根据上述需求定义衔接模块功能:在下游模块准备好后才让上游模块ctrl给出下一命令,否则等待。并完成读出的有效数据送到上游模块的任务。很简单,下游模块输出给控制模块一个信号rdy,当它为高电平时代表当前没有指令或者上一指令已响应完成。控制模块中指令计数器的原有计数条件和rdy==1条件逻辑与就完成了上述功能。这里需要特别注意的是:rdy信号必须以组合逻辑形式给出,否则由于rdy信号晚一拍输出,上游模块会出现误认情况。核心代码如下:
控制模块中:
1 //读写操作计数器 2 always @(posedge clk or negedge rst_n)begin 3 if(rst_n==1'b0)begin 4 rw_cnt <= 0; 5 end 6 else if(add_rw_cnt) begin 7 if(end_rw_cnt) 8 rw_cnt <= 0; 9 else 10 rw_cnt <= rw_cnt + 1; 11 end 12 end 13 14 assign add_rw_cnt = con_flag && rdy; 15 assign end_rw_cnt = add_rw_cnt && rw_cnt==RW_NUM-1; 16 17 //写使能 wr_flag和rd_flag由配置表给出 18 always @(posedge clk or negedge rst_n)begin 19 if(rst_n==1'b0)begin 20 wr_en <= 1'b0; 21 end 22 else if(add_rw_cnt && rw_cnt==0 && wr_flag)begin 23 wr_en <= 1'b1; 24 end 25 else begin 26 wr_en <= 1'b0; 27 end 28 end 29 30 //读使能 31 always @(posedge clk or negedge rst_n)begin 32 if(rst_n==1'b0)begin 33 rd_en <= 1'b0; 34 end 35 else if(add_rw_cnt && rw_cnt==1 && rd_flag)begin 36 rd_en <= 1'b1; 37 end 38 else begin 39 rd_en <= 1'b0; 40 end 41 end
衔接模块中:
1 //空闲输出 2 always@(*)begin 3 if(rd_en || wr_en || rd_com || wr_com) 4 rdy <= 0; 5 else 6 rdy <= 1; 7 end 8 9 //命令区间标志位 表示正在响应该命令 状态机实现时序接口模块情况 10 always@(posedge clk or negedge rst_n)begin 11 if(!rst_n) 12 wr_com <= 0; 13 else if(wr_en) 14 wr_com <= 1; 15 else if(wr_com && stop2idle) 16 wr_com <= 0; 17 end 18 19 always@(posedge clk or negedge rst_n)begin 20 if(!rst_n) 21 rd_com <= 0; 22 else if(rd_en) 23 rd_com <= 1; 24 else if(rd_com && stop2idle) 25 rd_com <= 0; 26 end 27 28 //地址更新 29 always@(posedge clk or negedge rst_n)begin 30 if(!rst_n) 31 addr_tmp <= 0; 32 else if(wr_en || rd_en) 33 addr_tmp <= addr; 34 end
到此,寄存器自动化配置中两个重点问题已然解决。本文是我在设计摄像头图像采集和以太网两个案例过程中总结所得。本人认为这种设计思想非常具有通用性,并不仅仅局限于这两个案例,因此单独提出,以备今后回顾和重用。