记录遇见的校招笔试题
频率计数器
`timescale 1ns/1ps
/*
若参考时钟为100MHz
检测频率范围1Hz~1MHz,由于参考时钟远快于被测时钟
现将被测时钟打三拍检测上升沿
然后再参考时钟域下计数
上升沿计数清零并保存计数器最大值
然后用参考时钟除以(计数最大值+1)得到被测时钟频率
若参考时钟频率为100MHz
检测频率范围1MHz到200MHZ或者1MZ~100MHz、1MHz~500MHz
先用被测时钟进行计数到199、99、499,,目的是为了降低被测时钟频率,方便100MHz去采样
0~99:1;100~199:0,其他亦然
将该信号打3拍进入参考时钟域
在高电平期间计数
在下降沿锁存计数最大值
然后计算频率
用参考时钟频率*高电平计数周期(被测时钟域下100)/ (高电平下参考时钟计数最大值+1)
*/
module frequency_counter(
input wire clk_100MHz, // 100 MHz reference clock
input wire reset, // Reset signal
input wire signal_in, // Input signal whose frequency needs to be measured
output reg [31:0] frequency // Measured frequency output
);
reg [31:0] counter_100MHz; // Counter for 100 MHz clock cycles
reg [31:0] signal_period; // Measured period of the input signal in 100 MHz clock cycles
/*
reg [31:0] signal_edge_count; // Edge count of input signal
reg signal_in_prev; // Previous state of the input signal
always @(posedge clk_100MHz or posedge reset) begin
if (reset) begin
counter_100MHz <= 32'd0;
signal_period <= 32'd0;
signal_in_prev <= 1'b0;
signal_edge_count <= 32'd0;
frequency <= 32'd0;
end else begin
// Count 100 MHz clock cycles
counter_100MHz <= counter_100MHz + 1;
// Detect rising edge of input signal
if (signal_in && !signal_in_prev) begin
// Capture the period of the signal in 100 MHz clock cycles
signal_period <= counter_100MHz;
counter_100MHz <= 32'd0; // Reset the 100 MHz counter
signal_edge_count <= signal_edge_count + 1; // Increment edge count
end
// Save the current state of the input signal
signal_in_prev <= signal_in;
// Calculate frequency only when we have measured at least one period
if (signal_period != 32'd0) begin
// Frequency = 100 MHz / signal period (in 100 MHz clock cycles)
frequency <= 100_000_000 / signal_period;
end
end
end
*/
reg [2:0] signal_in_prev;
reg out_flag;
always@(posedge clk_100MHz)begin
if(reset)begin
signal_in_prev <= 0;
end
else begin
signal_in_prev <= {signal_in_prev[1:0],signal_in};
end
end
always@(posedge clk_100MHz)begin
if(reset)begin
counter_100MHz <= 0;
signal_period <= 0;
counter_100MHz <= 0;
out_flag <= 0;
end
else if(signal_in_prev[2:1]==2'b01)begin
signal_period <= counter_100MHz;
counter_100MHz <= 0;
out_flag <= 1;
end
else begin
counter_100MHz <= counter_100MHz + 1;
signal_period <= signal_period;
out_flag <= 0;
end
end
always@(posedge clk_100MHz)begin
if(reset)begin
frequency <= 0;
end
else if(out_flag)begin
frequency <= 100_000_000 / (signal_period + 1);
end
else begin
frequency <= frequency;
end
end
endmodule
`timescale 1ns/1ps
module frequency_counter_tb;
// Parameters
localparam CLK_PERIOD = 10; // 100 MHz clock period (10 ns)
localparam RESET_TIME = 100; // Reset time in ns
// Testbench signals
reg clk_100MHz;
reg reset;
reg signal_in;
wire [31:0] frequency;
// Instantiate the frequency_counter module
frequency_counter uut (
.clk_100MHz(clk_100MHz),
.reset(reset),
.signal_in(signal_in),
.frequency(frequency)
);
// Generate 100 MHz clock
initial begin
clk_100MHz = 0;
forever #(CLK_PERIOD/2) clk_100MHz = ~clk_100MHz;
end
// Test sequence
initial begin
// Initialize signals
reset = 1;
signal_in = 0;
// Apply reset
#(RESET_TIME) reset = 0;
// Wait for reset to be deasserted
#(RESET_TIME);
// Generate a 1 kHz signal (period = 1 ms)
generate_signal(50_000);
// Wait some time to measure the frequency
#100_000; // 1 second in simulation time
// Check the measured frequency
$display("Measured frequency: %d Hz (Expected: 1000 Hz)", frequency);
// Generate a 10 kHz signal (period = 100 us)
generate_signal(600_000);
// Wait some time to measure the frequency
#100_000; // 100 ms in simulation time
// Check the measured frequency
$display("Measured frequency: %d Hz (Expected: 10000 Hz)", frequency);
// Generate a 1 MHz signal (period = 1 us)
generate_signal(200_000);
// Wait some time to measure the frequency
#100_000; // 10 ms in simulation time
// Check the measured frequency
$display("Measured frequency: %d Hz (Expected: 1000000 Hz)", frequency);
// End simulation
$stop;
end
// Task to generate a signal of a given frequency
task generate_signal;
input integer freq;
integer period;
begin
period = 1_000_000_000 / freq; // Period in ns
repeat(100) begin
signal_in = 1;
#(period/2);
signal_in = 0;
#(period/2);
end
end
endtask
endmodule
//第二种
`timescale 1ns/1ps
module freq_detect(
input clk_ref_in , //参考时钟50MHz
input clk_test_in , //待测时钟(范围1MHz~200MHz)
output [15:0] freq_num //输出端口频率值(单位:MHz)
);
//=====================register design===================
reg [7:0] clk_cnt = 0;
reg high_lev = 0;
reg [2:0] high_lev_r = 0;
reg [15:0] clk_ref_cnt = 0;
reg [15:0] clk_ref_cnt_lat = 0;
reg [15:0] clk_F = 0;
//=========================clk_test_in时钟域=============
//clk_in:1~200MHz,先降频,经200倍分频后的时钟不大于1MHz
//目的在于参考时钟50MHz,可以很好的采样到慢时钟
always@(posedge clk_test_in)begin
if(clk_cnt >= 199)begin
clk_cnt <= 0;
end
else begin
clk_cnt <= clk_cnt + 1;
end
end
//分频输出
always@(posedge clk_test_in)begin
if((clk_cnt == 99)||(clk_cnt == 199))begin
high_lev <= ~high_lev;
end
end
//================clk_ref_in时钟域=======================
//对high_lev打三拍同步到快时钟域
always@(posedge clk_ref_in)begin
high_lev_r <= {high_lev_r[1:0],high_lev};
end
//在ref时钟域下对分频后的高电平进行计数
always@(posedge clk_ref_in)begin
if(high_lev_r[2])begin
clk_ref_cnt <= clk_ref_cnt + 1;
end
else begin
clk_ref_cnt <= 0;
end
end
//用锁存器锁存高电平计数的最大值:即 分频时钟下降沿达到最大值
always@(posedge clk_ref_in)begin
if(high_lev_r[2:1]==2'b10)begin
clk_ref_cnt_lat <= clk_ref_cnt + 1;
end
end
// 计算输出频率
always@(posedge clk_ref_in)begin
clk_F <= 5000/clk_ref_cnt_lat;
end
assign freq_num = clk_F;
endmodule
`timescale 1ns/1ps
module freq_detect_tb();
reg clk_ref_in ;
reg clk_test_in ;
wire [15:0]freq_num ;
parameter REF_FERQ_PERIOD = 20;
parameter TEST_FERQ_PERIOD = 1000/20;
initial begin
clk_ref_in = 0;
forever begin
#(REF_FERQ_PERIOD/2);
clk_ref_in = ~clk_ref_in;
end
end
initial begin
clk_test_in = 0;
forever begin
#(TEST_FERQ_PERIOD/2);
clk_test_in = ~clk_test_in;
end
end
freq_detect uut(
.clk_ref_in (clk_ref_in ), //参考时钟50MHz
.clk_test_in (clk_test_in ), //待测时钟(范围1MHz~200MHz)
.freq_num (freq_num ) //输出端口频率值(单位:MHz)
);
endmodule