Featured image of post EDA 课程设计——手搓一个计时器

EDA 课程设计——手搓一个计时器

1
2
3
4
课设老师要求只到仿真那一步就可以了,后面的硬件部署全凭兴趣
学校没有fpga和开发板给本科生用,老师也不太懂开发板部署和vivado的使用
实在是太苦了,我借了一块7010跑,不会的只能自己查
感谢雪总的技术支持

一、实验名称

定时信号指示系统

二、实验目的

  • 复习和进一步理解课堂所学理论知识
  • 掌握基本组合、时序逻辑电路模块的 VerilogHDL 语言的描述方法
  • 熟悉 Vivado 软件的基本使用
  • 提高电路设计、代码调试和时序仿真的能力

三、实验任务

设计一个定时信号指示系统,该系统有三个设备,每个设备上有红、绿、蓝三个指示灯,用 VerilogHDL 语言设计一个定时控制系统,要求: 按时钟时间,
00时03分15秒:设备1绿灯亮 设备2红灯亮 设备3红灯亮
02时09分29秒:设备1蓝灯亮 设备2红灯亮 设备3红灯亮
04时20分33秒:设备1红灯亮 设备2绿灯亮 设备3红灯亮
06时19分14秒:设备1红灯亮 设备2蓝灯亮 设备3红灯亮
08时25分50秒:设备1红灯亮 设备2红灯亮 设备3绿灯亮
10时13分17秒:设备1红灯亮 设备2红灯亮 设备3蓝灯亮
00时03分15秒:设备1绿灯亮 设备2红灯亮 设备3红灯亮
这样依次循环。

FPGA 外输入时钟为8Hz,在 FPGA 分频为1Hz后,设计数字时钟,然后根据时间向外发不同的控制信号,控制三个设备灯的亮灭如下图所示:

Img

四、实验过程

(一)总体思路

该实验任务实现思路较为清晰,主要分为分频器、计数器、判断逻辑等模块。

Img

1、分频器

显而易见,我们需要一个50% 8分频模块。

2、计数器

在计数器模块中,如何平衡好时分秒的计数输出是关键。级联计数器每个单元模值太小会使引脚过于繁琐不直观,太大则会耗费太多系统资源。

这里我构造了模12同步计数器和模5同步计数器,分别使用1组模12同步计数器和模5同步计数器完成秒的计数,使用一组模12同步计数器和模5同步计数器完成分的计数,最后使用一个模12同步计数器完成时的计数。

计数器分别输出 s0, s1, m0, m1, h0 五个 4bit 信号送入判断逻辑。

Img

3、判断逻辑

该模块接收来自 s0, s1, m0, m1, h0 等5个 4bit 信号,并在对应时刻输出对应的 R1, G1, B1, R2, G2, B2, R3, G3, B3 等9个RGB信号。设定 RGB 信号低电平点亮对应颜色 LED。

Img

(二)代码实现

1、分频器(已修改)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
module clk_div8(
    input clk,
    input rst_n,
    output reg clk_div
);    
    parameter NUM_DIV = 8;
    reg [3:0] cnt;

always @(posedge clk or negedge rst_n)
    if(!rst_n) 
        begin
        cnt     <= 1'd0;
        clk_div <= 1'b0;
        end
    else
        if(cnt < NUM_DIV / 2 - 1)
            begin
            cnt     <= cnt + 1'b1;
            clk_div <= clk_div;
            end
        else
            begin
            cnt     <= 1'd0;
            clk_div <= ~clk_div;
            end

endmodule

2、计数器

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//模5计数器模块
module cnt5(clk, rst_n, en, dout, co, set, set_flag);

input clk, rst_n, en;
input set_flag;
input [3:0]set;
output [3:0] dout;
reg [3:0] dout;
output co;

always@(posedge clk or negedge rst_n)
begin
   if(!rst_n)
   	    if(set_flag)
   		dout <= set[3:0];
   	    else
   		dout <= dout;              //系统置位
   else 
    if(en)
   	    if(dout == 4'b0100)        //计数值达到5时,计数器清零
   		    dout <= 4'b0000;
   	    else
   		    dout <= dout + 1'b1;   //否则,计数器加1
        else
   	        dout <= dout;
end

assign co = dout[0];               //当计数达到4(4'b0100)时,进位为1,计数值为其他,都没有进位

endmodule
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//模12计数器模块
module cnt12(clk, rst_n, en, dout, co, set, set_flag);

input clk, rst_n, en;
input set_flag;
input [3:0]set;
output [3:0] dout;
reg [3:0] dout;
output co;

always@(posedge clk or negedge rst_n)
begin
   if(!rst_n)
   	    if(set_flag)
   		dout <= set[3:0];
   	    else
   		dout <= dout;                //系统置位
   else 
        if(en)
   	        if(dout == 4'b1011)      //计数值达到9时,计数器清零
   		        dout <= 4'b0000;
   	        else
   		        dout <= dout + 1'b1; //否则,计数器加1
        else
   	        dout <= dout;
end

assign co = dout[0]&dout[1]&dout[3]; //当计数达到11(4'b1011)时,进位为1,计数值为其他,都没有进位

endmodule

3、判断逻辑

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
//共阳极接发,低电平点亮LED
module fsm(s0, s1, m0, m1, h0, r1, g1, b1, r2, g2, b2, r3, g3, b3);

input s0, s1, m0, m1, h0;
output reg r1, g1, b1, r2, g2, b2, r3, g3, b3;

always@(s0, s1, m0, m1, h0)
begin
if ((s0==3)&(s1==1)&(m0==3)&(m1==0)&(h0==0))
    r1=1'b1; g1=1'b0; b1=1'b1;
    r2=1'b0; g2=1'b1; b2=1'b1;
    r3=1'b0; g3=1'b1; b3=1'b1;
else
    if ((s0==5)&(s1==2)&(m0==9)&(m1==0)&(h0==2))
        r1=1'b1; g1=1'b1; b1=1'b0;
        r2=1'b0; g2=1'b1; b2=1'b1;
        r3=1'b0; g3=1'b1; b3=1'b1;
    else
        if ((s0==9)&(s1==2)&(m0==8)&(m1==4)&(h0==1))
            r1=1'b0; g1=1'b1; b1=1'b1;
            r2=1'b1; g2=1'b0; b2=1'b1;
            r3=1'b0; g3=1'b1; b3=1'b1;
        else
            if ((s0==2)&(s1==1)&(m0==7)&(m1==1)&(h0==6))
                r1=1'b0; g1=1'b1; b1=1'b1;
                r2=1'b1; g2=1'b1; b2=1'b0;
                r3=1'b0; g3=1'b1; b3=1'b1;
            else
                if ((s0==2)&(s1==4)&(m0==1)&(m1==2)&(h0==8))
                    r1=1'b0; g1=1'b1; b1=1'b1;
                    r2=1'b0; g2=1'b1; b2=1'b1;
                    r3=1'b1; g3=1'b0; b3=1'b1;
                else
                    if ((s0==5)&(s1==1)&(m0==1)&(m1==1)&(h0==10))
                        r1=1'b0; g1=1'b1; b1=1'b1;
                        r2=1'b0; g2=1'b1; b2=1'b1;
                        r3=1'b1; g3=1'b1; b3=1'b0;
                    else
                        r1=1'b0; g1=1'b0; b1=1'b0;
                        r2=1'b0; g2=1'b0; b2=1'b0;
                        r3=1'b0; g3=1'b0; b3=1'b0;
end

endmodule

4、顶层设计

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
module top
(
CLK,
RST, EN, SET, SET_FLAG,
R1, G1, B1, R2, G2, B2, R3, G3, B3
);
    
input CLK;
input RST, EN, SET_FLAG;
input [3:0] SET;
output wire R1, G1, B1, R2, G2, B2, R3, G3, B3;

wire line_clk_div;
wire line_s0_s1;
wire [3:0] line_s0;
wire line_s1_m0;
wire [3:0] line_s1;
wire line_m0_m1;
wire [3:0] line_m0;
wire line_m1_h0;
wire [3:0] line_m1;
wire [3:0] line_h0;

clk_div8 DIVIDER(
.clk(CLK),
.rst_n(RST),
.clk_div(line_clk_div)
);

cnt12 COUNTER_S0(
.clk(line_clk_div),
.rst_n(RST),
.en(EN),
.set(SET),
.set_flag(SET_FLAG),
.dout(line_s0),
.co(line_s0_s1)
);

cnt5 COUNTER_S1(
.clk(line_s0_s1),
.rst_n(RST),
.en(EN),
.set(SET),
.set_flag(SET_FLAG),
.dout(line_s1),
.co(line_s1_m0)
);

cnt12 COUNTER_M0(
.clk(line_s1_m0),
.rst_n(RST),
.en(EN),
.set(SET),
.set_flag(SET_FLAG),
.dout(line_m0),
.co(line_m0_m1)
);

cnt5 COUNTER_M1(
.clk(line_m0_m1),
.rst_n(RST),
.en(EN),
.set(SET),
.set_flag(SET_FLAG),
.dout(line_m1),
.co(line_m1_h0)
);

cnt12 COUNTER_H0(
.clk(line_m1_h0),
.rst_n(RST),
.en(EN),
.set(SET),
.set_flag(SET_FLAG),
.dout(line_h0)
);

fsm FSM(
.s0(line_s0),
.s1(line_s1),
.m0(line_m0),
.m1(line_m1),
.h0(line_h0),
.r1(R1),
.g1(G1),
.b1(B1),
.r2(R2),
.g2(G2),
.b2(B2),
.r3(R3),
.g3(G3),
.b3(B3)
);
endmodule

(三)纯代码仿真及优化

缩短仿真时间长度,提前判断逻辑对应的时间点,进行初步仿真。

testbench(已修改)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
`timescale 1us / 1us
module test_top;
//--------------------------------------------------------------------------
//--    Ports
//--------------------------------------------------------------------------
//Inputs
reg CLK;
reg RST, EN, SET_FLAG;
reg [3:0] SET;
//Outputs
wire R1, G1, B1, R2, G2, B2, R3, G3, B3;

//--------------------------------------------------------------------------
//--    Instantiation
//--------------------------------------------------------------------------
top uut(
.CLK(CLK),
.RST(RST),
.EN(EN),
.SET(SET),
.SET_FLAG(SET_FLAG),
.R1(R1), .G1(G1), .B1(B1),
.R2(R2), .G2(G2), .B2(B2),
.R3(R3), .G3(G3), .B3(B3)
);

//--------------------------------------------------------------------------
//--    CLK
//--------------------------------------------------------------------------
initial
	begin
	CLK=0;
	forever
	#62500 CLK=~CLK;
    end

//--------------------------------------------------------------------------
//--    RST, EN, SET, SET_FLAG
//--------------------------------------------------------------------------
initial
    begin
    RST=1;
    EN=0;
    # 10000;
    EN=1;
    SET=4'b0000;
    SET_FLAG=0;
    end
endmodule

在仿真过程中,大部分功能基本能实现,但也存在一些问题:

1、timescale问题

在 testbench 若使用

1
`timescale 1s / 1us

搭配时钟翻转

1
forever #0.0625 CLK=~CLK;

会出现无报错,但是 clk 跑不起来的情况

Img

网上查找诸多资料无解后,将时钟翻转改为

1
forever #1 CLK=~CLK;

发现默认时间单位是ps,也就是说0.0625 * 1s没有生效

Img

使用

1
`timescale 1us / 1us

搭配时钟翻转

1
forever #62500 CLK=~CLK;

则可以正常运行clk

Img

怀疑并不是代码不规范,而是 Vivado 自身bug。

原先那句timescale在 2023.1 版本无法运行,更新到 2023.2 就可以了。

2、进位对齐问题

因在光标处进位出现提前,故改为采用下降沿触发进位

Img

现在clk是低电平先行,故所有时钟、进位都应改为下降沿标准

3、类似越沿采样的问题

进位对齐的问题解决了,开头还是有提前采样(反越沿采样?): 此时不能再改下降沿逻辑了,只能用延时控制使能信号,使程序不要一开始就进入工作状态,即EN端不要直接置1。

Img

延缓使能后,该问题得到解决:

Img

Img

(四)纯代码仿真最终测试

仿真时间长度恢复到12小时、模拟实际判定条件,观察仿真结果。

需要注意的是,使能端因延缓启动会有10ms的手动延时

Img

抽取 36797s 看一下对应是否是 10:13:17

Img

确实是对应(但由于延时使能,该仿真时间线会有10ms滞后,可计算进去)

(五)纯代码仿真结果集中展示

1、Simulation

Img

2、Schematic

Img

Img

3、I/O Planning

Img

4、Flooorplanning

Img

Img

(六)开发板配置

Img

Img

Img

Img

目前我已在本次课设基础上,完成了开发板时钟分频降频、I/O管脚约束等后续工作,对所设计的模块进行了 IP 核封装,并基于全 IP 的架构完成了电路综合和实现。

后续我将继续在 zynq7010 上实现相关功能。


ok考试考完了,课设的内容也只是要求到仿真。

如果要迁移到板子上,我需要解决以下2个问题:

  • 板载时钟降频(50MHz –> 8Hz)
  • 合理管脚约束(常数引脚不悬空)

(七)板载时钟降频

50MHz –> 8Hz 这个分频率实在是有点大,肯定是采用多级分频。需要注意:

  • 高频区段的时钟精度要保证
  • 考虑到资源问题,每一级分频数不能太大
  • 为设计简单,可以复用模块

下面的设计步骤就是基于 IP 核的组合了。使用 IP 核的首要原因是我当时并没有想到什么在板上锁定常数引脚的方法,但是 IP 核中有一个常数 IP,可以接上引脚,于是便想用那个试一试。

因此在考虑时钟问题的时候我也准备使用基于 IP 核组合的设计流程。

综上所述,我选择了 Clocking Wizard 做一级分频以保证精度。 Clocking Wizard 的输出范围约为 5MHz ~ 800Mhz,所以能降则降,一级分频结果为 5MHz。 次级分频使用的是1个10分频和2个250分频。我也将之前纯代码仿真的那部分整体封装成 IP,最后一起连接。

1、50% 250分频模块

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
`timescale 1us / 1us
//////////////////////////////////////////////////////////////////////////////////

module freq_divider_250(clk, rst_n, clk_div);
    input clk;
    input rst_n;
    output reg clk_div=1'b1;
 
    parameter NUM_DIV = 250;
    reg [7:0] cnt;

always @(negedge clk or negedge rst_n)
    if(!rst_n) 
        begin
        cnt     <= 1'd0;
        clk_div <= 1'b0;
        end
    else
        if(cnt < NUM_DIV / 2 - 1)
            begin
            cnt     <= cnt + 1'b1;
            clk_div <= clk_div;
            end
        else
            begin
            cnt     <= 1'd0;
            clk_div <= ~clk_div;
            end
endmodule

testbench

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
`timescale 1us / 1us
//////////////////////////////////////////////////////////////////////////////////

module test_freq_divider_250( );
//--------------------------------------------------------------------------
//--    Ports
//--------------------------------------------------------------------------
//Inputs
reg clk;
reg rst_n;
//Outputs
wire clk_div;
//--------------------------------------------------------------------------
//--    Instantiation
//--------------------------------------------------------------------------
freq_divider_250 test_freq_divider_250(
.clk(clk),
.rst_n(rst_n),
.clk_div(clk_div)
);
//--------------------------------------------------------------------------
//--    CLK
//--------------------------------------------------------------------------
initial
	begin
	clk=0;
	forever
	#250 clk=~clk;
    end
//--------------------------------------------------------------------------
//--     RST, EN, SET, SET_FLAG
//--------------------------------------------------------------------------
initial
    begin
    rst_n=1;
    end
endmodule

仿真

Img

封装

Img

2、50% 10分频模块

照葫芦画瓢,仿真

Img

封装

Img

最终2个分频模块都能在 IP Catalog 中查询到

Img

(八)Constant IP 核约束常数管脚

直接在 IP Catalog 中调用已经封装好的 Constant IP 核。

Clocking Wizard 也是一样调用

(九)接线图

Img

(十)输出端口管脚约束

CLK 输入接 H16,所有 I/O 端口采用 LVCMOS33 电平标准。

Img

(十一)Generate Block Design Diagram 到代码

block design contains locked ips

更新IP核状态

Img

Create HDL Wrapper

最终的 Sources 目录为:

Img

(十二)第二阶段仿真

testbench

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
`timescale 1ps / 1ps
//////////////////////////////////////////////////////////////////////////////////
module test_design_1(  );
//--------------------------------------------------------------------------
//--    Ports
//--------------------------------------------------------------------------
//Inputs
reg clk_in_50M;
//--------------------------------------------------------------------------
//--    Instantiation
//--------------------------------------------------------------------------
design_1 test_design_1(
.clk_in_50M(clk_in_50M));
//--------------------------------------------------------------------------
//--    clk_in_50M
//--------------------------------------------------------------------------
initial
	begin
	clk_in_50M=0;
	forever
	#10000 clk_in_50M=~clk_in_50M;
    end
endmodule

波形

Img

Img

Img

但是似乎仿真不了太长时间,毕竟 50MHz 的时钟和 1ps 的精度让我本就不富裕的 CPU 计算能力更显捉襟见肘

于是乎,我把那几个判断时间改短一点,象征性地在板子上验证一下。

核心代码修改:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
begin//--------------------------------------if_else判断的条件改简单了,每1秒变一下信号
if ((s0==1)&(s1==0)&(m0==0)&(m1==0)&(h0==0))
    begin
    r1=1'b1; g1=1'b0; b1=1'b1;
    r2=1'b0; g2=1'b1; b2=1'b1;
    r3=1'b0; g3=1'b1; b3=1'b1;
    end
else
    if ((s0==2)&(s1==0)&(m0==0)&(m1==0)&(h0==0))
        begin
        r1=1'b1; g1=1'b1; b1=1'b0;
        r2=1'b0; g2=1'b1; b2=1'b1;
        r3=1'b0; g3=1'b1; b3=1'b1;
        end
    else
        if ((s0==3)&(s1==0)&(m0==0)&(m1==0)&(h0==0))
            begin
            r1=1'b0; g1=1'b1; b1=1'b1;
            r2=1'b1; g2=1'b0; b2=1'b1;
            r3=1'b0; g3=1'b1; b3=1'b1;
            end
        else
            if ((s0==4)&(s1==0)&(m0==0)&(m1==0)&(h0==0))
                begin
                r1=1'b0; g1=1'b1; b1=1'b1;
                r2=1'b1; g2=1'b1; b2=1'b0;
                r3=1'b0; g3=1'b1; b3=1'b1;
                end
            else
                if ((s0==5)&(s1==0)&(m0==0)&(m1==0)&(h0==0))
                    begin
                    r1=1'b0; g1=1'b1; b1=1'b1;
                    r2=1'b0; g2=1'b1; b2=1'b1;
                    r3=1'b1; g3=1'b0; b3=1'b1;
                    end
                else
                    if ((s0==6)&(s1==0)&(m0==0)&(m1==0)&(h0==10))
                        begin
                        r1=1'b0; g1=1'b1; b1=1'b1;
                        r2=1'b0; g2=1'b1; b2=1'b1;
                        r3=1'b1; g3=1'b1; b3=1'b0;
                        end
                    else
                        begin
                        r1=1'b0; g1=1'b0; b1=1'b0;
                        r2=1'b0; g2=1'b0; b2=1'b0;
                        r3=1'b0; g3=1'b0; b3=1'b0;
                        end
end //------------------------------------------------------------------------------

仿真波形(象征性的搞了几秒)

Img

封装好,替换原来的main0

Img

连上新的IP核

Img

引出输出端口

Img

重新generate一下,重新create wrapper

Img

可以看到.v文件更新了

Img

简单仿真2s康康 emm失败了

实现

I/O Planning

Img

Floorplanning

Img

生成比特流

报错:

1
[DRC UCIO-1] Unconstrained Logical Port: 1 out of 10 logical ports have no user assigned specific location constraint (LOC). This may cause I/O contention or incompatibility with the board power or connectivity affecting performance, signal integrity or in extreme cases cause damage to the device or the components to which it is connected. 

管脚约束不恰当,修改之

Img

Auto Connect 之后 Pragram 一下,完美点亮

Img

Img

Img

Img

Img

Img

但是最后一个状态好像没出来,不知道为什么呜呜呜……

未完待续……

六、实验感想

本次实践感想主要分为以下几个方面:

首先我十分感谢老师的教学和指导,这让我掌握了 FPGA 基本的基本理论知识和 VerilogHDL 的基本编程、调试方法。在本次实践过程中我遇到了一些问题,没有老师的指导,这些问题就不会被顺利解决。

其次,这此实践锻炼了我创建并实施项目的能力。我克服畏惧,独立完成了从总体设计、模块设计到 debug、仿真、综合等全部环节。面对一个个 error,我没有灰心丧气,而是一个一个排查错误,积极上网查找资料、找大佬沟通交流。这些对于一年前怕写代码、怕 debug、怕跟人沟通的我来说是一种进步。

最后,我深刻地认识到,在课程体系落后于其他以电子信息为专长的学校的情况下,只有自力更生,学会理解迁移经典代码,学会合理运用网络,学会与更强的人沟通,才能在短时间内上手一份自己并不熟悉的工程。

Built with Hugo
主题 StackJimmy 设计
# /layouts/partials/footer/custom.html