【随笔】“真OO无双”前辈提供的SEG7_Controller模块的问题改进

本文分享了一种FPGA七段显示器控制器的设计方案,并详细记录了在DE3—150开发板上的实现过程及遇到的问题。针对低位向高位进位时出现的错误现象,通过对代码的分析和修改成功解决了问题。

  首先,非常感谢“真OO无双”前辈,在学习FPGA过程中给与了极大帮助。在此记录一些问题,希望能帮助别人,也给自己留点记录,方便以后查找。

      前辈在(原創) 如何設計一個七段顯示器Controller? 中给了出了一个例子,如下:

我的板子是DE3—150,上面有两颗数码管。将其作为自定义组建加入到SOPC Builder中(具体参考(原創) 如何設計一個七段顯示器Controller? ),其参数修改为:

 


  
1 module SEG7_Controller (
2 // Avalon clock interface siganals
3   input csi_clockreset_clk,
4 input csi_clockreset_reset_n,
5 // Signals for Avalon-MM slave port
6   input [ADDR_WIDTH - 1 : 0 ] avs_s1_address,
7 input avs_s1_write,
8 input [DATA_WIDTH - 1 : 0 ] avs_s1_writedata,
9 // user logic inputs and outputs
10   output [SEG7_NUM * 8 - 1 : 0 ] avs_s1_export_oHEX
11 );
12
13 parameter DATA_WIDTH = 32 ; // data bus width
14   parameter SEG7_NUM = 8 ; // specify the number of seg7 unit
15   parameter ADDR_WIDTH = 3 ; // log2(SEG7_NUM)
16  
17 reg [SEG7_NUM - 1 : 0 ] base_index;
18 reg [DATA_WIDTH - 1 : 0 ] write_data;
19 reg [(SEG7_NUM * 8 - 1 ): 0 ] reg_file;
20
21 assign avs_s1_export_oHEX = ~ reg_file;
22
23 always@(posedge csi_clockreset_clk, negedge csi_clockreset_reset_n) begin
24 if ( ! csi_clockreset_reset_n) begin
25 integer i;
26 base_index <= 8 ' h00;
27 write_data <= 8 ' h00;
28
29 for (i = 0 ; i < SEG7_NUM * 8 ; i = i + 1 )
30 reg_file[i] <= 1 ' b1;
31 end
32 else begin
33 if (avs_s1_write) begin
34 integer j;
35 write_data <= avs_s1_writedata;
36 base_index <= (avs_s1_address << 3 );
37
38 for (j = 0 ; j < 8 ; j = j + 1 )
39 reg_file[base_index + j] <= write_data[j];
40 end
41 end
42 end
43
44 endmodule

 

 

 


  
SEG7_NUM = 2

ADDR_WIDTH
= 1

 

编译下载到板子上,然后在Nios II中运行测试程序,在低位向高位进位时出现错误现象:

...08—>09—>00—>11—>12...19—>10—>21...

也就是说,低位向高位进位时,低位清零,高位没变化,下一次更新时,高低位一起更新。

  分析代码33~39行,由于使用阻塞赋值,reg_file,base_index以及write_data中的数据其实都是前一时钟周期的,那么其中数值变化如下:

 

修改代码如下,问题解决。

 


  
1 module SEG7_Controller (
2 // Avalon clock interface siganals
3 input csi_clockreset_clk,
4 input csi_clockreset_reset_n,
5 // Signals for Avalon-MM slave port
6 input [ADDR_WIDTH - 1 : 0 ] avs_s1_address,
7 input avs_s1_write,
8 input [ 31 : 0 ] avs_s1_writedata,
9 // user logic inputs and outputs
10 output [SEG7_NUM * 8 - 1 : 0 ] avs_s1_export_oHEX
11 );
12
13 parameter SEG7_NUM = 8 ; // specify the number of seg7 unit
14 parameter ADDR_WIDTH = 3 ; // log2(SEG7_NUM)
15 parameter DEFAULT_ACTIVE = 1 ;
16 parameter LOW_ACTIVE = 1 ;
17
18 reg [(SEG7_NUM * 8 - 1 ): 0 ] reg_file;
19
20 assign avs_s1_export_oHEX = (LOW_ACTIVE) ?~ reg_file:reg_file;
21
22 always@(posedge csi_clockreset_clk, negedge csi_clockreset_reset_n) begin
23 if ( ! csi_clockreset_reset_n) begin
24 reg_file[(SEG7_NUM * 8 - 1 ): 0 ] <= (DEFAULT_ACTIVE) ? 8 ' hFF:8 ' h00; // trun on or off
25 end
26 else begin
27 if (avs_s1_write) begin
28 integer j;
29 for (j = 0 ; j < 8 ; j = j + 1 )
30 reg_file[(avs_s1_address << 3 ) + j] <= avs_s1_writedata[j];
31 // reg_file[((avs_s1_address << 3) + 7 ):(avs_s1_address << 3)] <= avs_s1_writedata[7:0];
32 end
33 end
34 end
35
36 endmodule

 

 

方法二,还可以在起另外一个always块,在其中完成write_data对reg_file的赋值,因为clk非常快,不受avs_s1_write信号控制,可以避免此情况。

 

总结:

  • reg变量,相当于触发器,若采用阻塞赋值,传递出去的其实是其存放的前一时钟周期的值。
  • 制作自定义组建时,对寄存器的读写,应该与寄存器功能实现分开,如方法2. 

 

 

 

 

 

 

 

 

 

转载于:https://www.cnblogs.com/xiyi/archive/2010/11/03/1868427.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值