10、安路SparkRoad国产FPGA测评【实战篇】等精度频率计
本帖最后由 1nnocent 于 2022-8-10 00:00 编辑<p> 依照测评计划该实战篇的功能是实现等精度频率计,并通过串口显示测量值,编写完代码后发现资源不够用,需要换芯片,手头只有安路FPGA,所以只能将显示方式换成数码管显示。写完代码提示如下错误:</p>
<p> 通过查找软件手册可以知道错误原因,如果不进行改代码的话,要实现此功能需要换用其他资源更大的芯片。</p>
<p></p>
<p> 这里考虑了一下为什么会占用这么多mslice资源,尝试将串口显示模块注释掉后编译就通过了,串口需要显示的字符为“_ _ _ _ _ _k H z \r \n”一共十二个字符,每个字符占八bit。每位数字都是用case语句从0、1、2、3、4、5、6、7、8、9十个数字中根据具体数值选择,一共需要6个case语句,最耗费资源的是这6个case语句。所以该实验改用数码管进行显示。以下为串口显示的代码:</p>
<pre>
<code>`timescale 1ns / 1ps
module uart_tx_display(
input sys_clk, //system clock positive
input rst_n, //reset ,low active
input fre_test,
output uart_tx //fpga send data
);
parameter CLK_FRE = 200; //Mhz
localparam IDLE =0;
localparam SEND =1; //send HELLO ALINX\r\n
localparam WAIT =2; //wait 1 second and send uart received data
reg tx_data; //sending data
reg tx_str;
reg tx_data_valid; //sending data valid
reg display;
wire tx_data_ready; //singal for sending data
reg tx_cnt;
reg wait_cnt;
reg state;
/*************************************************************************
generate single end clock
**************************************************************************/
reg case_num;
always@(posedge sys_clk or negedge rst_n)
begin
if(rst_n == 1'b0)
begin
display <= 80'd0;
end
else
begin
case_num <= fre_test % 10; // [ 3:0 ]
case_num <= fre_test / 10 % 10; // [ 7:4 ]
case_num <= fre_test / 100 % 10; //
case_num <= fre_test / 1000 % 10;//
case_num[ 7:4 ] <= fre_test / 10000 % 10; // [ 7:4 ]
case_num[ 3:0 ] <= fre_test / 100000 % 10;// [ 3:0 ]
case(case_num[ 3:0 ])
8'd0: display <= "0" ;
8'd1: display <= "1" ;
8'd2: display <= "2" ;
8'd3: display <= "3" ;
8'd4: display <= "4" ;
8'd5: display <= "5" ;
8'd6: display <= "6" ;
8'd7: display <= "7" ;
8'd8: display <= "8" ;
8'd9: display <= "9" ;
default:display <= 8'd0;
endcase
case(case_num[ 7:4 ])
8'd0: display <= "0" ;
8'd1: display <= "1" ;
8'd2: display <= "2" ;
8'd3: display <= "3" ;
8'd4: display <= "4" ;
8'd5: display <= "5" ;
8'd6: display <= "6" ;
8'd7: display <= "7" ;
8'd8: display <= "8" ;
8'd9: display <= "9" ;
default:display <= 8'd0;
endcase
case(case_num)
8'd0: display <= "0" ;
8'd1: display <= "1" ;
8'd2: display <= "2" ;
8'd3: display <= "3" ;
8'd4: display <= "4" ;
8'd5: display <= "5" ;
8'd6: display <= "6" ;
8'd7: display <= "7" ;
8'd8: display <= "8" ;
8'd9: display <= "9" ;
default:display <= 8'd0;
endcase
display <= "_";
case(case_num)
8'd0: display <= "0" ;
8'd1: display <= "1" ;
8'd2: display <= "2" ;
8'd3: display <= "3" ;
8'd4: display <= "4" ;
8'd5: display <= "5" ;
8'd6: display <= "6" ;
8'd7: display <= "7" ;
8'd8: display <= "8" ;
8'd9: display <= "9" ;
default:display <= 8'd0;
endcase
case(case_num)
8'd0: display <= "0" ;
8'd1: display <= "1" ;
8'd2: display <= "2" ;
8'd3: display <= "3" ;
8'd4: display <= "4" ;
8'd5: display <= "5" ;
8'd6: display <= "6" ;
8'd7: display <= "7" ;
8'd8: display <= "8" ;
8'd9: display <= "9" ;
default:display <= 8'd0;
endcase
case(case_num)
8'd0: display <= "0" ;
8'd1: display <= "1" ;
8'd2: display <= "2" ;
8'd3: display <= "3" ;
8'd4: display <= "4" ;
8'd5: display <= "5" ;
8'd6: display <= "6" ;
8'd7: display <= "7" ;
8'd8: display <= "8" ;
8'd9: display <= "9" ;
default:display <= 8'd0;
endcase
end
end
/*************************************************************************
1 second sends a packet HELLO ALINX\r\n , FPGA has been receiving state
****************************************************************************/
always@(posedge sys_clk or negedge rst_n)
begin
if(rst_n == 1'b0)
begin
wait_cnt <= 32'd0;
tx_data <= 8'd0;
state <= IDLE;
tx_cnt <= 8'd0;
tx_data_valid <= 1'b0;
end
else
case(state)
IDLE:
state <= SEND;
SEND:
begin
wait_cnt <= 32'd0;
tx_data <= tx_str;
if(tx_data_valid == 1'b1 && tx_data_ready == 1'b1 && tx_cnt < 8'd13)//Send 12 bytes data
begin
tx_cnt <= tx_cnt + 8'd1; //Send data counter
end
else if(tx_data_valid && tx_data_ready)//last byte sent is complete
begin
tx_cnt <= 8'd0;
tx_data_valid <= 1'b0;
state <= WAIT;
end
else if(~tx_data_valid)
begin
tx_data_valid <= 1'b1;
end
end
WAIT:
begin
wait_cnt <= wait_cnt + 32'd1;
if(wait_cnt >= CLK_FRE * 1000000)
state <= SEND;
end
default:
state <= IDLE;
endcase
end
/*************************************************************************
combinational logicSend "HELLO ALINX\r\n"
****************************************************************************/
always@(*)
begin
case(tx_cnt)
8'd0 :tx_str <= display[ 7:0 ];
8'd1 :tx_str <= display;
8'd2 :tx_str <= display;
8'd3 :tx_str <= display;
8'd4 :tx_str <= display;
8'd5 :tx_str <= display;
8'd6 :tx_str <= display;
8'd7 :tx_str <= " " ;
8'd8 :tx_str <= "k" ;
8'd9 :tx_str <= "H" ;
8'd10:tx_str <= "z" ;
8'd11:tx_str <= "\r";
8'd12:tx_str <= "\n";
default:tx_str <= 8'b0;
endcase
end
/***************************************************************************
calling uart_tx module and uart_rx module
****************************************************************************/
uart_tx#
(
.CLK_FRE(CLK_FRE),
.BAUD_RATE(115200)
) uart_tx_inst
(
.clk (sys_clk ), //clock input
.rst_n (rst_n ), //asynchronous reset input, low active
.tx_data (tx_data ), //data to send
.tx_data_valid (tx_data_valid ), //data to be sent is valid
.tx_data_ready (tx_data_ready ), //send ready
.tx_pin (uart_tx ) //serial data output
);
endmodule
</code></pre>
<p> </p>
<p> 简单介绍以下等精度频率计测量原理:电路中是检测上升沿来计数,所以只能计整数。因为计数不精确的问题,所以会有一定的误差,这个误差的大小为N±1,其中N为Na或Nb,或都有误差;传统的测量方法是取晶振分频后得到门控信号,然后数这段时间内的待测信号的数目,从而获得待测信号的频率。这样的测量方法可以获得精确的Nb和带有±1误差的Na。则误差为:</p>
<p style=""> 若我们根据待测信号来取门控信号,从而将这个±1的误差放在Nb上,则误差为:</p>
<p style=""> </p>
<p>而由频率的关系知,Nb一般至少为Na的十倍以上。故相比第一种方法,第二种方法测量方法可以极大地减小误差。</p>
<p style="">第二种方法测量的相对误差为与待测信号无关,故又称等精度法。 </p>
<p style=""> </p>
<p> gate模块:该模块用于产生gate信号,gate信号以测量频率为基准,计数4096个周期的测量信号,在这4096个计数周期中gate信号置高,其余情况下置低。模块中定义cnt进行计数,cnt的位数为13位,计数8192次,刚好一半用于gate信号产生,一半将gate信号置低,这样也方便写程序,只需让cnt自加,前4096次置高,后4096次置低。gate模块代码:</p>
<pre>
<code>`timescale 1ns / 1ps
module gate(
input rst_n,
input test_clk,
outputreg gate
);
localparam TEST_CNT = 4096;
reg cnt;
always@(posedge test_clk or negedge rst_n)begin
if(!rst_n)begin
cnt <= 13'd0;
gate <= 1'b0;
end
else if(cnt < TEST_CNT) begin
gate <= 1'b1;
cnt <= cnt + 1'b1;
end
else begin
gate <= 1'b0;
cnt <= cnt + 1'b1;
end
end
endmodule</code></pre>
<p> </p>
<p> clk_cnt:该模块用于gate信号置高期间一共经过了几个系统时钟周期,该模块定义了clk_cnt寄存器用于计算gate置高期间经过的系统时钟周期数,clk_cnt在cal_valid上升沿到来时清零,cal_valid为后级模块计算测量频率完成之后的标志。clk_cnt_valid为系统时钟个数计数完成标志(后级计算频率时使用),gate信号为低时完成计数。clk_cnt模块代码:</p>
<pre>
<code>`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 2022/08/08 22:48:01
// Design Name:
// Module Name: clk_cnt
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module clk_cnt(
input clk,
input rst_n,
input gate,
input cal_valid, // calculation done
output reg clk_cnt,
output clk_cnt_valid // enable to calculation
);
reg gated0,gated1;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
gated0 <= 1'b0;
gated1 <= 1'b1;
end
else begin
gated0 <= gate;
gated1 <= gated0;
end
end
assign clk_cnt_valid = gated0 && (~gated1);
reg cal_valid0,cal_valid1;
wire cal_valid_pos;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cal_valid0 <= 1'b0;
cal_valid1 <= 1'b1;
end
else begin
cal_valid0 <= cal_valid;
cal_valid1 <= cal_valid0;
end
end
assign cal_valid_pos = (~cal_valid0) && cal_valid1;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
clk_cnt <= 32'd0;
end
else if(cal_valid_pos)begin
clk_cnt <= 32'd0;
end
else if(gate)begin
clk_cnt <= clk_cnt + 1'b1;
end
end
endmodule
</code></pre>
<p> </p>
<p> calculation:该模块根据被测时钟的个数、系统时钟的个数以及系统时钟,通过等精度频率计公式计算出频率。该模块代码如下:</p>
<pre>
<code>`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 2022/08/09 20:57:33
// Design Name:
// Module Name: calculation
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module calculation(
input clk,
input rst_n,
input clk_cnt,
input clk_cnt_valid, // enable to calculation
output reg freq,
output reg cal_valid
);
parameter SYS_CLK = 24;
parameter CLK_CNT = 4096;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
freq <= 32'd0;
cal_valid <= 1'b0;
end
else if(clk_cnt_valid)begin
freq <= (CLK_CNT * SYS_CLK / clk_cnt);
cal_valid <= 1'b1;
end
else
cal_valid <= 1'b0;
end
endmodule
</code></pre>
<p> seg_4:该模块为数码管显示频率模块。该模块将calculation模块计算出来的频率值在数码管显示,数码管具体怎么显示这里不再赘述,显示方式在前面学习篇部分由介绍。该部分把历程计时器的四位段选信号对应到频率的个位、十位、百位和千位进行显示。该模块代码如下:</p>
<pre>
<code>`timescale 1ns/ 1ps
// --------------------------------------------------------------------
// >>>>>>>>>>>>>>>>>>>>>>>>> COPYRIGHT NOTICE <<<<<<<<<<<<<<<<<<<<<<<<<
// --------------------------------------------------------------------
//
// Author: Anlogic
//
// Description:
//
// ������ ��ʾ
//
// Web: www.anlogic.com
// --------------------------------------------------------------------
module seg4(
input wire clk_24m, // 系统时钟
input wire rst_n,
input freq,
output wire sm_seg, // 数码管段选信号
output wire sm_bit // 数码管位选信号
);
//reg addr;
reg sm_bit1_num;
reg sm_bit2_num;
reg sm_bit3_num;
reg sm_bit4_num;
//��������ԼΪ10msɨ��һ��
reg cnt_w;
//������λѡ
reg sm_bit_reg;
reg sm_seg_num ;
reg sm_seg_reg;
localparam
S0 = 4'b0000 ,
S1 = 4'b0001 ,
S2 = 4'b0010 ,
S3 = 4'b0011 ,
S4 = 4'b0100 ,
S5 = 4'b0101 ,
S6 = 4'b0110 ,
S7 = 4'b0111 ,
S8 = 4'b1000 ,
S9 = 4'b1001 ;
always@(posedge clk_24m or negedge rst_n) // 小数点后一位
begin
if(!rst_n)
sm_bit1_num <= 4'h0;
else begin
sm_bit1_num <= freq % 10;
end
end
always@(posedge clk_24m or negedge rst_n) // 小数点后一位
begin
if(!rst_n)
sm_bit2_num <= 4'h0;
else begin
sm_bit2_num <= freq /10 % 10;
end
end
always@(posedge clk_24m or negedge rst_n) // 小数点后一位
begin
if(!rst_n)
sm_bit3_num <= 4'h0;
else begin
sm_bit3_num <= freq /100 % 10;
end
end
always@(posedge clk_24m or negedge rst_n) // 小数点后一位
begin
if(!rst_n)
sm_bit4_num <= 4'h0;
else begin
sm_bit4_num <= freq /1000;
end
end
always@(posedge clk_24m or negedge rst_n)
begin
if(!rst_n)
cnt_w <= 18'd0;
else if(cnt_w == 18'b111_111_111_111_111_111)// 0.1s 1/24M*262144
// else if(&cnt_w)
cnt_w <= 18'd0;
else
cnt_w <= cnt_w + 1;
end
always@(posedge clk_24m or negedge rst_n)
begin
if(!rst_n)
sm_seg_num <= 4'h0;
else
begin
case( cnt_w )
2'b00:sm_seg_num <= sm_bit1_num;
2'b01:sm_seg_num <= sm_bit2_num;
2'b10:sm_seg_num <= sm_bit3_num;
2'b11:sm_seg_num <= sm_bit4_num;
endcase
end
end
always@(posedge clk_24m or negedge rst_n)
begin
if(!rst_n)
sm_bit_reg <= 4'b1111;
else
begin
case( cnt_w )
2'b00:sm_bit_reg <= 4'b1110;
2'b01:sm_bit_reg <= 4'b1101;
2'b10:sm_bit_reg <= 4'b1011;
2'b11:sm_bit_reg <= 4'b0111;
endcase
end
end
always@(*)
begin
case ( sm_seg_num )
S0:
sm_seg_reg <= 8'hc0;
S1:
sm_seg_reg <= 8'hf9;
S2:
sm_seg_reg <= 8'ha4;
S3:
sm_seg_reg <= 8'hb0;
S4:
sm_seg_reg <= 8'h99;
S5:
sm_seg_reg <= 8'h92;
S6:
sm_seg_reg <= 8'h82;
S7:
sm_seg_reg <= 8'hf8;
S8:
sm_seg_reg <= 8'h80;
S9:
sm_seg_reg <= 8'h90;
default:sm_seg_reg <= 8'hc0;
endcase
end
assign sm_seg = sm_seg_reg;
assign sm_bit = sm_bit_reg;
endmodule
</code></pre>
<p> PLL:该模块用于产生被测信号,一共产生5中被测信号。</p>
<p> debounce:按键模块主要是通过一个按键对被测信号进行切换,一共五种,按键每按下一次就切换一种频率进行测量。</p>
<p> freq_test:该模块为顶层模块,代码如下:</p>
<pre>
<code>`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 2022/08/08 22:12:23
// Design Name:
// Module Name: freq_test
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module freq_test(
input clk,
input rst_n,
input key,
output wire sm_seg, // 数码管段选信号
output wire sm_bit // 数码管位选信号
);
wire test_clk;
wire gate;
wire clk0_out,clk1_out,clk2_out,clk3_out,clk4_out;
reg change;
assign test_clk = change == 3'd0 ? clk0_out : change == 3'd1 ? clk1_out :
change == 3'd2 ? clk2_out : change == 3'd3 ? clk3_out : clk4_out;
PLL u_PLL(
.refclk(clk),
.reset(~rst_n),
.clk0_out(clk0_out),
.clk1_out(clk1_out),
.clk2_out(clk2_out),
.clk3_out(clk3_out),
.clk4_out(clk4_out)
);
wire key_pulse;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
change <= 3'd0;
end
else if(key_pulse)begin
change <= change + 1'b1;
end
else if(change == 3'd5)
change <= 3'd0;
end
debounce u_debounce(
.clk(clk), // 输入时钟
.rst_n(rst_n), // 复位
// key
.key(key), // 按键输入
.key_pulse(key_pulse) // 按键按下的信号
);
gate u_gate(
.rst_n(rst_n),
.test_clk(test_clk),
.gate(gate)
);
wire cal_valid;
wire clk_cnt;
wire clk_cnt_valid;
clk_cnt u_clk_cnt(
.clk(clk),
.rst_n(rst_n),
.gate(gate),
.cal_valid(cal_valid), // calculation done
.clk_cnt(clk_cnt),
.clk_cnt_valid(clk_cnt_valid) // enable to calculation
);
wire freq;
calculation u_calculation(
.clk(clk),
.rst_n(rst_n),
.clk_cnt(clk_cnt),
.clk_cnt_valid(clk_cnt_valid), // enable to calculation
.freq(freq),
.cal_valid(cal_valid)
);
seg4 u_seg4(
.clk_24m(clk), // 系统时钟
.rst_n(rst_n),
.freq(freq),
.sm_seg(sm_seg), // 数码管段选信号
.sm_bit(sm_bit) // 数码管位选信号
);
endmodule
</code></pre>
<div> 这里附上源码:</div>
<p> 以下为实验效果:</p>
<p>e0c9e8f8cf27dc12e491f50cedf82972<br />
</p>
<p>资源不够是什么意思?内存不够?</p>
wangerxian 发表于 2022-8-10 16:57
资源不够是什么意思?内存不够?
<p>不知道啊,软件手册就写了mslice数目超了</p>
<p> </p>
<p> </p>
<p>。</p>
wangerxian 发表于 2022-8-10 16:57
资源不够是什么意思?内存不够?
<p><a href="https://blog.csdn.net/fpga_start/article/details/122393339" target="_blank">https://blog.csdn.net/fpga_start/article/details/122393339</a></p>
<p>赛灵思也有slice的介绍,应该是一样的</p>
<p> </p>
<p> </p>
<p>。</p>
<p> sm_bit1_num <= <span style="color:#e74c3c;">freq % 10;</span><br />
sm_bit2_num <= <span style="color:#c0392b;">freq /10 % 10</span>;<br />
sm_bit3_num <= <span style="color:#e74c3c;">freq /100 % 10</span>;<br />
sm_bit4_num <= <span style="color:#e74c3c;">freq /1000</span>;<br />
居然用组合逻辑做除法,必然消耗很多资源了。</p>
cruelfox 发表于 2022-8-15 09:59
sm_bit1_num <= freq % 10;
& ...
<p>要不然串口不好显示数字,除法可以改用移位试试,当时不知道是除法这边耗费资源比较多</p>
<p> </p>
<p> </p>
<p>。</p>
cruelfox 发表于 2022-8-15 09:59
sm_bit1_num <= freq % 10;
& ...
<p>好像也不对啊,这个代码是数码管显示的版本,串口版本和数码管版本都是用除法算的</p>
<p> </p>
<p> </p>
<p>。</p>
cruelfox 发表于 2022-8-15 09:59
sm_bit1_num <= freq % 10;
& ...
<p>不过串口版本使用了六次除法,数码管使用了三次除法,后面可以试试把串口版本也改成三次除法的方式。</p>
页:
[1]