10. Anlu SparkRoad domestic FPGA evaluation [practical] equal precision frequency meter
[Copy link]
This post was last edited by 1nnocent on 2022-8-10 00:00
According to the evaluation plan, the function of this practical chapter is to realize an equal-precision frequency meter and display the measured value through the serial port. After writing the code, it was found that the resources were insufficient and the chip needed to be changed. There was only an Anlu FPGA on hand, so the display mode could only be changed to a digital tube display. After writing the code, the following error was prompted:
The cause of the error can be found by looking up the software manual. If the code is not modified, it will be necessary to use a chip with larger resources to implement this function.
Here I thought about why it takes up so many mslice resources. I tried to comment out the serial port display module and then compiled it. The characters that the serial port needs to display are "_ _ _ _ _ _k H z \r \n", a total of twelve characters, each character occupies eight bits. Each digit is selected from 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 according to the specific value using the case statement. A total of 6 case statements are required, and these 6 case statements are the most resource-consuming. Therefore, this experiment uses a digital tube for display. The following is the code for serial port display:
`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
The following is a brief introduction to the measurement principle of an equal-precision frequency meter: The circuit detects the rising edge to count, so only integers can be counted. Because of the inaccurate counting problem, there will be a certain error, the size of this error is N±1, where N is Na or Nb, or both have errors; the traditional measurement method is to obtain the gate signal after dividing the crystal oscillator, and then count the number of signals to be measured during this period of time to obtain the frequency of the signal to be measured. This measurement method can obtain accurate Nb and Na with an error of ±1. The error is:
If we take the gate signal according to the signal to be measured, and put this ±1 error on Nb, the error is:
From the relationship between frequency and measurement, we know that Nb is generally at least ten times greater than Na. Therefore, compared with the first method, the second measurement method can greatly reduce the error.
The relative error measured by the second method is
, which is independent of the signal to be measured, so it is also called the equal precision method.
Gate module: This module is used to generate gate signals. The gate signal is based on the measurement frequency. It counts 4096 cycles of the measurement signal. In these 4096 counting cycles, the gate signal is set high, and in the rest of the cases it is set low. The module defines cnt for counting. The number of bits of cnt is 13. It counts 8192 times, just half of which is used to generate the gate signal, and the other half is used to set the gate signal low. This is also convenient for writing programs. You only need to let cnt increment by itself, set it high for the first 4096 times, and set it low for the last 4096 times. Gate module code:
`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: This module is used to count the number of system clock cycles that have passed during the gate signal being set high. This module defines the clk_cnt register to calculate the number of system clock cycles that have passed during the gate being set high. clk_cnt is cleared when the cal_valid rising edge arrives. cal_valid is the flag after the subsequent module has completed the frequency measurement calculation. clk_cnt_valid is the system clock count completion flag (used when the subsequent module calculates the frequency). The count is completed when the gate signal is low. clk_cnt module 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 [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: This module calculates the frequency based on the number of measured clocks, the number of system clocks, and the system clock using the equal precision frequency meter formula. The module code is as follows:
`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: This module is a digital tube display frequency module. This module displays the frequency value calculated by the calculation module on the digital tube. The specific display method of the digital tube will not be described here. The display method has been introduced in the previous learning section. This part corresponds the four-bit segment selection signal of the history timer to the ones, tens, hundreds and thousands of the frequency for display. The module code is as follows:
`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: This module is used to generate the measured signal, a total of 5 measured signals are generated.
debounce: The key module mainly switches the measured signal through a key. There are five types in total. Each time the key is pressed, a frequency is switched for measurement.
freq_test: This module is the top-level module, and the code is as follows:
`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
The following are the experimental results:
freq_test
|