首先,非常感谢“真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
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 ;
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
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.