10、安路SparkRoad国产FPGA测评【实战篇】等精度频率计
[复制链接]
本帖最后由 1nnocent 于 2022-8-10 00:00 编辑
依照测评计划该实战篇的功能是实现等精度频率计,并通过串口显示测量值,编写完代码后发现资源不够用,需要换芯片,手头只有安路FPGA,所以只能将显示方式换成数码管显示。写完代码提示如下错误:
通过查找软件手册可以知道错误原因,如果不进行改代码的话,要实现此功能需要换用其他资源更大的芯片。
这里考虑了一下为什么会占用这么多mslice资源,尝试将串口显示模块注释掉后编译就通过了,串口需要显示的字符为“_ _ _ _ _ _k H z \r \n”一共十二个字符,每个字符占八bit。每位数字都是用case语句从0、1、2、3、4、5、6、7、8、9十个数字中根据具体数值选择,一共需要6个case语句,最耗费资源的是这6个case语句。所以该实验改用数码管进行显示。以下为串口显示的代码:
`timescale 1ns / 1ps
module uart_tx_display(
input sys_clk, //system clock positive
input rst_n, //reset ,low active
input [31:0 ] 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[7:0] tx_data; //sending data
reg[7:0] tx_str;
reg tx_data_valid; //sending data valid
reg [55:0 ] display;
wire tx_data_ready; //singal for sending data
reg[7:0] tx_cnt;
reg[31:0] wait_cnt;
reg[3:0] state;
/*************************************************************************
generate single end clock
**************************************************************************/
reg [47:0] 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[23:20] <= fre_test % 10; // [ 3:0 ] [23:20]
case_num[19:16] <= fre_test / 10 % 10; // [ 7:4 ] [19:16]
case_num[15:12] <= fre_test / 100 % 10; // [11:8 ] [15:12]
case_num[11:8 ] <= fre_test / 1000 % 10; // [15:12] [11:8 ]
case_num[ 7:4 ] <= fre_test / 10000 % 10; // [19:16] [ 7:4 ]
case_num[ 3:0 ] <= fre_test / 100000 % 10;// [23:20] [ 3:0 ]
case(case_num[ 3:0 ])
8'd0: display[7:0] <= "0" ;
8'd1: display[7:0] <= "1" ;
8'd2: display[7:0] <= "2" ;
8'd3: display[7:0] <= "3" ;
8'd4: display[7:0] <= "4" ;
8'd5: display[7:0] <= "5" ;
8'd6: display[7:0] <= "6" ;
8'd7: display[7:0] <= "7" ;
8'd8: display[7:0] <= "8" ;
8'd9: display[7:0] <= "9" ;
default:display[7:0] <= 8'd0;
endcase
case(case_num[ 7:4 ])
8'd0: display[15:8 ] <= "0" ;
8'd1: display[15:8 ] <= "1" ;
8'd2: display[15:8 ] <= "2" ;
8'd3: display[15:8 ] <= "3" ;
8'd4: display[15:8 ] <= "4" ;
8'd5: display[15:8 ] <= "5" ;
8'd6: display[15:8 ] <= "6" ;
8'd7: display[15:8 ] <= "7" ;
8'd8: display[15:8 ] <= "8" ;
8'd9: display[15:8 ] <= "9" ;
default:display[15:8 ] <= 8'd0;
endcase
case(case_num[11:8 ])
8'd0: display[23:16] <= "0" ;
8'd1: display[23:16] <= "1" ;
8'd2: display[23:16] <= "2" ;
8'd3: display[23:16] <= "3" ;
8'd4: display[23:16] <= "4" ;
8'd5: display[23:16] <= "5" ;
8'd6: display[23:16] <= "6" ;
8'd7: display[23:16] <= "7" ;
8'd8: display[23:16] <= "8" ;
8'd9: display[23:16] <= "9" ;
default:display[23:16] <= 8'd0;
endcase
display[31:24] <= "_";
case(case_num[15:12])
8'd0: display[39:32] <= "0" ;
8'd1: display[39:32] <= "1" ;
8'd2: display[39:32] <= "2" ;
8'd3: display[39:32] <= "3" ;
8'd4: display[39:32] <= "4" ;
8'd5: display[39:32] <= "5" ;
8'd6: display[39:32] <= "6" ;
8'd7: display[39:32] <= "7" ;
8'd8: display[39:32] <= "8" ;
8'd9: display[39:32] <= "9" ;
default:display[39:32] <= 8'd0;
endcase
case(case_num[19:16])
8'd0: display[47:40] <= "0" ;
8'd1: display[47:40] <= "1" ;
8'd2: display[47:40] <= "2" ;
8'd3: display[47:40] <= "3" ;
8'd4: display[47:40] <= "4" ;
8'd5: display[47:40] <= "5" ;
8'd6: display[47:40] <= "6" ;
8'd7: display[47:40] <= "7" ;
8'd8: display[47:40] <= "8" ;
8'd9: display[47:40] <= "9" ;
default:display[47:40] <= 8'd0;
endcase
case(case_num[23:20])
8'd0: display[55:48] <= "0" ;
8'd1: display[55:48] <= "1" ;
8'd2: display[55:48] <= "2" ;
8'd3: display[55:48] <= "3" ;
8'd4: display[55:48] <= "4" ;
8'd5: display[55:48] <= "5" ;
8'd6: display[55:48] <= "6" ;
8'd7: display[55:48] <= "7" ;
8'd8: display[55:48] <= "8" ;
8'd9: display[55:48] <= "9" ;
default:display[55:48] <= 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 logic Send "HELLO ALINX\r\n"
****************************************************************************/
always@(*)
begin
case(tx_cnt)
8'd0 : tx_str <= display[ 7:0 ];
8'd1 : tx_str <= display[15:8 ];
8'd2 : tx_str <= display[23:16];
8'd3 : tx_str <= display[31:24];
8'd4 : tx_str <= display[39:32];
8'd5 : tx_str <= display[47:40];
8'd6 : tx_str <= display[55:48];
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
简单介绍以下等精度频率计测量原理:电路中是检测上升沿来计数,所以只能计整数。因为计数不精确的问题,所以会有一定的误差,这个误差的大小为N±1,其中N为Na或Nb,或都有误差;传统的测量方法是取晶振分频后得到门控信号,然后数这段时间内的待测信号的数目,从而获得待测信号的频率。这样的测量方法可以获得精确的Nb和带有±1误差的Na。则误差为:
若我们根据待测信号来取门控信号,从而将这个±1的误差放在Nb上,则误差为:
而由频率的关系知,Nb一般至少为Na的十倍以上。故相比第一种方法,第二种方法测量方法可以极大地减小误差。
第二种方法测量的相对误差为
与待测信号无关,故又称等精度法。
gate模块:该模块用于产生gate信号,gate信号以测量频率为基准,计数4096个周期的测量信号,在这4096个计数周期中gate信号置高,其余情况下置低。模块中定义cnt进行计数,cnt的位数为13位,计数8192次,刚好一半用于gate信号产生,一半将gate信号置低,这样也方便写程序,只需让cnt自加,前4096次置高,后4096次置低。gate模块代码:
`timescale 1ns / 1ps
module gate(
input rst_n,
input test_clk,
output reg gate
);
localparam TEST_CNT = 4096;
reg [12: 0] 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
clk_cnt:该模块用于gate信号置高期间一共经过了几个系统时钟周期,该模块定义了clk_cnt寄存器用于计算gate置高期间经过的系统时钟周期数,clk_cnt在cal_valid上升沿到来时清零,cal_valid为后级模块计算测量频率完成之后的标志。clk_cnt_valid为系统时钟个数计数完成标志(后级计算频率时使用),gate信号为低时完成计数。clk_cnt模块代码:
`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 [31:0] 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
calculation:该模块根据被测时钟的个数、系统时钟的个数以及系统时钟,通过等精度频率计公式计算出频率。该模块代码如下:
`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 [31:0] clk_cnt,
input clk_cnt_valid, // enable to calculation
output reg [31:0] 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
seg_4:该模块为数码管显示频率模块。该模块将calculation模块计算出来的频率值在数码管显示,数码管具体怎么显示这里不再赘述,显示方式在前面学习篇部分由介绍。该部分把历程计时器的四位段选信号对应到频率的个位、十位、百位和千位进行显示。该模块代码如下:
`timescale 1ns/ 1ps
// --------------------------------------------------------------------
// >>>>>>>>>>>>>>>>>>>>>>>>> COPYRIGHT NOTICE <<<<<<<<<<<<<<<<<<<<<<<<<
// --------------------------------------------------------------------
//
// Author: Anlogic
//
// Description:
//
// ������ ��ʾ
//
// Web: www.anlogic.com
// --------------------------------------------------------------------
module seg4(
input wire clk_24m, // 系统时钟
input wire rst_n,
input [31:0] freq,
output wire [7:0] sm_seg, // 数码管段选信号
output wire [3:0] sm_bit // 数码管位选信号
);
//reg [3:0] addr;
reg [3:0] sm_bit1_num;
reg [3:0] sm_bit2_num;
reg [3:0] sm_bit3_num;
reg [3:0] sm_bit4_num;
//��������ԼΪ10msɨ��һ��
reg [17:0] cnt_w;
//������λѡ
reg [3:0] sm_bit_reg;
reg [3:0] sm_seg_num ;
reg [7:0] 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[17:16] )
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[17:16] )
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
PLL:该模块用于产生被测信号,一共产生5中被测信号。
debounce:按键模块主要是通过一个按键对被测信号进行切换,一共五种,按键每按下一次就切换一种频率进行测量。
freq_test:该模块为顶层模块,代码如下:
`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 [7:0] sm_seg, // 数码管段选信号
output wire [3:0] sm_bit // 数码管位选信号
);
wire test_clk;
wire gate;
wire clk0_out,clk1_out,clk2_out,clk3_out,clk4_out;
reg [2:0] 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 [31:0] 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 [31:0] 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
以下为实验效果:
freq_test
|