启动信号,下降启动一轮操作
8 output[3:0] coe_rom_addr;//参与卷积的固定信号rom地址信号
9 output[3:0] rd_data_dpram_addr;//读取双口RAM缓冲中数据的地址信号
10 output acc_clr; //累加器清零信号,高电平用于清除MAC电路的结果,准备下一次卷积计算
11 output wren; //写使能信号,每个start周期向下一个DPRAM的缓冲(本级的缓冲区也可以使用这个写入信号)里写入一个新数据的使能信号
12 output[3:0] wr_data_dpram_addr;//写入DPRAM缓冲区的新数据的地址线
13 output flag;//标志信号,表示卷积电路在工作
14 output mac_en;//乘加器的使能信号,为1时每个上升沿执行乘加
15 wire flag;//状态标志,为1时表示正在进行卷积操作,否则表示没有操作
16 reg[9:0] flag_cnt;//状态计数器,start信号到来后还是计数,当其大于一定值后切换标志
17 reg[3:0] current_pt;//当前位置指针,会在每次start到来卷积后加一,以指向下一次数据缓冲器的首地址
18 assign acc_clr = start;
19 assign mac_en = ((flag_cnt[9:0]>=10'd3)&(flag_cnt[9:0]<=10'd20))? 1'b1 : 1'b0;//只有当flag_cnt等于3时系数和dpram才能读出所需的数据,注意读锁存器造成的一个周期的延迟。
20 ///////向后续卷积节传递最老的一个数据的控制信号
21 assign wren = (flag_cnt[9:0] == 10'd20)? 1'b1 : 1'b0;//这里要看仿真时序图才能明白,flag_cnt=20的周期刚好读出本轮以后不再使用的数据
22 assign wr_data_dpram_addr[3:0] = current_pt[3:0];
23 always @ (posedge start or negedge rst_n)
24 begin
25 if(!rst_n)
26 current_pt[3:0] <= 4'b1111;//复位后为最大值,第一个start脉冲使其从0开始
27 else
28 current_pt[3:0] <= current_pt[3:0] + 4'd1;//首地址缓冲器,会在每次采样(或start)信号到来后加一,为下次输入新数据移动缓冲器做好准备
29 end
30 assign flag = ((flag_cnt[9:0]>=10'd2)&(flag_cnt[9:0]<=10'd20))? 1'b1 : 1'b0;
31 always @ (negedge clk or posedge start)//注意为了让flag从clk下降沿开始,以上升沿为每个周期的中心,这里用用flag的下降沿计数
32 begin
33 if(start)
34 begin
35 flag_cnt[9:0] <= 10'd0;
36 end
37 else begin
38 if(flag_cnt[9:0] <= (10'd16 + 10'd1 + 10'd3))
39 //计到15再加1,则一共1-16个脉冲;加1是为了留出第一个时钟周期作为dpram的地址潜伏期;加2是为了等待乘法器的潜伏期(潜伏期为3,但最后一个不用乘加)
40 begin
41 flag_cnt[9:0] <= flag_cnt[9:0] +1'd1;
42 end
43 else
44 begin
45 flag_cnt[9:0] <= flag_cnt[9:0];
46 end
47 end
48 end
49 pset_addr_cnt i_rd_addr_cnt(//例化读缓冲当前地址计数器
50 .rst_n(rst_n),
51 .load(!flag),//每产生一个启动信号,就要重新对首地址置新值
52 .clk(clk),
53 .pre_value(current_pt),
54 .addr_cnt(rd_data_dpram_addr)
55 );
56 addr_cnt i_coe_rom_addr_cnt(//例化系数地址产生计数器
57 .en(flag),
58 .clk(clk),
59 .addr_cnt(coe_rom_addr)
60 );
61 endmodule
62
63 module addr_cnt(en,clk,addr_cnt);
64 //本模块产生4位计数值,作为地址产生器
65 input en;//高电平有效
66 input clk;
67 output[3:0] addr_cnt;
68 reg[3:0] addr_cnt;
69 always @ (negedge clk or negedge en)//为了使地址再时钟下降沿产生,上升沿位于数据中央,clk使用了下降沿
70 begin
71 if(!en)
72 addr_cnt[3:0] <= 4'd0;
73 else
74 addr_cnt[3:0] <= addr_cnt[3:0] + 4'd1;
75 end
76 endmodule
77
78 //带预置功能的地址计数器,4位
79 module pset_addr_cnt(rst_n,load,clk,pre_value,addr_cnt);
80 input rst_n;
81 input load;//load高电平时同步加载初值
82 input clk;
83 input[3:0] pre_value;//输入的预置初值
84 output[3:0] addr_cnt;//计数输出
85 reg[3:0] addr_cnt;
86 always @(negedge clk or negedge rst_n)//为了使地址再时钟下降沿产生,上升沿位于数据中央,clk使用了下降沿
87 begin
88 if(!rst_n)
89 addr_cnt[3:0] <= 4'd0;//pre_value[3:0];
90 else begin
91 if(load == 1'b1)
92 addr_cnt[3:0] <= pre_value[3:0];
93 else
94 addr_cnt[3:0] <= addr_cnt[3:0] + 4'd1;
95 end
96 end
97 endmodule
View Code
上述代码中,需要特别注意的是数据缓冲区“当前地址寄存器”current_pt[3:0],它会在每个采样点的计算卷积运算开始之前,在启动信号start驱动下自动加一,随后current_pt[3:0]作为初值加载给数据缓冲区读地址寄存器/计数器“rd_data_dpram_addr”,从而起到了修正环形队列首地址的目的。这一操作保证了数据缓冲区的首地址指针总是指向环形队列数据结构中最新的数据。
另外,写使能信号wren是每个卷积节向后级的卷积节输出的控制信 |