FPGA 校招笔试题详解

作者:默宸 来源:FPGA技术联盟

看电路图画波形

本文转载自CSDN李锐博恩Reborn某校招题目,仅仅给出下面电路图,要求画出Q1,Q2以及Q3的波形,并描述电路功能。

可以看出,这个电路很简单,或许你会纠结于初值是什么,可是仔细想想,该电路其实和初值没有关系,第一个触发器的输入是第二个以及第三个触发器的输出的反馈,是Q1与Q2的或非,只要触发器复位有初值即可,一般触发器复位初值为0,这里也默认为0,那么输入值在复位时应该为1.

正常运行时(复位无效),q0的第一个值为复位值延迟一拍并持续一个时钟,之后q1、q2就简单了。

为了清晰的分析这个问题,我们把这个电路用Verilog描述出来,之后验证。

`timescale 1ns / 1ps

//////////////////////////////////////////////////////////////////////////////////

// Company:

//

// Create Date: 2019/07/24 19:57:47

// Design Name:

// Module Name: test

//////////////////////////////////////////////////////////////////////////////////

module test(

input rst_n,

input clk,

output out2

);

wire in1;

reg q0, q1, q2;

always@(posedge clk or negedge rst_n) begin

if(!rst_n) begin

q0 <= 1'b0;

q1 <= 1'b0;

q2 <= 1'b0;

end

else begin

q0 <= in1;

q1 <= q0;

q2 <= q1;

end

end

assign in1 = !q0 & !q1;

assign out2 = q2;

endmodule

生成对应RTL原理图如下:

可见,和题目所给的电路图几乎一致。在仿真验证:

仿真验证:

`timescale 1ns / 1ps

//////////////////////////////////////////////////////////////////////////////////

// Company:

// Engineer: reborn lee

//

// Create Date: 2019/07/24 20:19:38

// Design Name:

// Module Name: sim_test

//////////////////////////////////////////////////////////////////////////////////

module sim_test(

);

reg clk;

reg rst_n;

wire out2;

initial begin

clk = 0;

forever

#2 clk = ~clk;

end

initial begin

rst_n = 1'b0;

#5

rst_n = 1'b1;

end

test test_tb(

.rst_n(rst_n),

.clk(clk),

.out2(out2)

);

endmodule

观察波形图,与我们分析的一致。

任意切换的时钟分频电路

题目要求写出代码实现以下功能,任意切换1-8分频,且无论奇分频还是偶分频,占空比均为50%。

电路设计步骤分为两部分:

第一:

确定整体架构,包括输入输出:如何任意切换?我写了一个分频模块,其中一个输入为div,范围为1-8。

input clk,

input [3:0] div,

input rst_n,

output clk_out

第二:

确定分频实现电路,分频大家都会写,实现过程可分为奇分频与偶分频的实现,两种的实现方法是不一样的,偶分频简单,计数即可,奇分频需要三个步骤,第一、计数,第二、下降沿采样,第三、相与或相或,具体实现方法后面再说。

确定好输入输出之后,还需要一个使能信号,当输入分频述div后,使能某一种分频模式使能,有8种分频模式,当然不能傻傻地设置8个使能变量,en1,en2,...,en8;这样很不好看,而且不利于后面程序编写,那怎么办呢?我的方式是:

reg [7:0] fre_en;

每次只能使能一位,fre_en[i]使能代表i+1分频模式有效。

通过div的值来确认某种模式使能,其实现如下:

localparam DIV1 = 1, DIV2 = 2, DIV3 = 3, DIV4 = 4;

localparam DIV5 = 5, DIV6 = 6, DIV7 = 7, DIV8 = 8;

reg [7:0] fre_en;

always@(posedge clk or negedge rst_n) begin

if(~rst_n) begin

fre_en <= 0;

end

else begin

case(div)

DIV1: fre_en <= 8'b0000_0001;

DIV2: fre_en <= 8'b0000_0010;

DIV3: fre_en <= 8'b0000_0100;

DIV4: fre_en <= 8'b0000_1000;

DIV5: fre_en <= 8'b0001_0000;

DIV6: fre_en <= 8'b0010_0000;

DIV7: fre_en <= 8'b0100_0000;

DIV8: fre_en <= 8'b1000_0000;

endcase

end

end

奇分频和偶分频,二者均有计数过程,但是这里想做出一点改变,因为1分频等于没分频,2分频直接对输出时钟取反即可,其他分频和计数有关。

我们写一个计数器,用于其他分频模式,8分频需要计数次数最多,我们设计数器位数为4位,其实3位就够了。

根据模式使能确定计数次数。

//计数模块

reg [3:0] fre_cnt;

always @(posedge clk or negedge rst_n) begin

if(~rst_n) begin

fre_cnt <= 0;

end

else begin

case(1'b1)

fre_en[0]: begin

;

end

fre_en[1]: begin

;

end

fre_en[2]: begin //3分频计数

if(fre_cnt < 2) fre_cnt <= fre_cnt + 1;

else fre_cnt <= 0;

end

fre_en[3]: begin

if(fre_cnt < 3) fre_cnt <= fre_cnt + 1;

else fre_cnt <= 0;

end

fre_en[4]: begin

if(fre_cnt < 4) fre_cnt <= fre_cnt + 1;

else fre_cnt <= 0;

end

fre_en[5]: begin

if(fre_cnt < 5) fre_cnt <= fre_cnt + 1;

else fre_cnt <= 0;

end

fre_en[6]: begin

if(fre_cnt < 6) fre_cnt <= fre_cnt + 1;

else fre_cnt <= 0;

end

fre_en[7]: begin

if(fre_cnt < 7) fre_cnt <= fre_cnt + 1;

else fre_cnt <= 0;

end

endcase

end

end

有了计数模块,就可以设计分频实现模块了,根据分频使能来确定分频模式,不同的分频模式对应不同的分频实现方式。

reg clk_out_r;

reg clk_even;

always @(posedge clk or negedge rst_n) begin

if(~rst_n) begin

clk_even <= 0;

clk_out_r <= 0;

end

else begin

case(1'b1)

fre_en[0]: begin //1 分频

;

end

fre_en[1]: begin //2 分频

clk_even <= ~clk_even;

end

fre_en[2]: begin //3 分频

if(fre_cnt == 1) clk_out_r <= ~clk_out_r;

else if(fre_cnt == 2) clk_out_r <= ~clk_out_r;

else clk_out_r <= clk_out_r;

end

fre_en[3]: begin //4 分频

if(fre_cnt == 1) clk_even <= ~clk_even;

else if(fre_cnt ==3) clk_even <= ~clk_even;

else clk_even <= clk_even;

end

fre_en[4]: begin //5分频

if(fre_cnt == 2) clk_out_r <= ~clk_out_r;

else if(fre_cnt == 4) clk_out_r <= ~clk_out_r;

else clk_out_r <= clk_out_r;

end

fre_en[5]: begin // 6分频

if(fre_cnt == 2) clk_even <= ~clk_even;

else if(fre_cnt == 5) clk_even <= ~clk_even;

else clk_even <= clk_even;

end

fre_en[6]: begin //7 分频

if(fre_cnt == 3) clk_out_r <= ~clk_out_r;

else if(fre_cnt == 6) clk_out_r <= ~clk_out_r;

else clk_out_r <= clk_out_r;

end

fre_en[7]: begin //8 分频

if(fre_cnt == 3) clk_even <= ~clk_even;

else if(fre_cnt == 7) clk_even <= ~clk_even;

else clk_even <= clk_even;

end

endcase

end

end

考虑到奇数分频最后的输出clk_out需要一段组合逻辑,所以需要使能wire类型。这就导致这个分频实现模块里不能直接使用clk_out作为左值。用clk_even用作偶分频输出clk_out的缓冲;至于clk_out_r,以及clk_out_rr是为了奇分频而声明的。

至于偶分频,则需要计数一半反转一次,计数结束反转一次即可。好了,下面继续解决奇分频的下降沿采样问题:

//下降沿采样模块

reg clk_out_rr;

always @(negedge clk or negedge rst_n) begin

if(~rst_n) begin

clk_out_rr <= 0;

end

else begin

case(1'b1)

fre_en[0]: ;

fre_en[1]: ;

fre_en[2]: begin

clk_out_rr <= clk_out_r;

end

fre_en[3]: ;

fre_en[4]: begin

clk_out_rr <= clk_out_r;

end

fre_en[5]: ;

fre_en[6]: begin

clk_out_rr <= clk_out_r;

end

fre_en[7]: ;

endcase

end

end

最后输出分频时钟:

//产生分频时钟

assign clk_out = ( fre_en[0] | fre_en[1] | fre_en[3] | fre_en[5] | fre_en[7] )? clk_even : clk_out_r | clk_out_rr;

先给出一个仿真,假如输入div为5,则进行5分频,则:

假如输入div为6则进行6分频:

先5分频在1分频:

最后给出完整版程序以及测试。

完整版程序:

//任意切换1——8分频电路设计(某芸科技),无论是奇分频还是偶分频,占空比均为50%

module Fre_Div(

input clk,

input [3:0] div,

input rst_n,

output clk_out

);

localparam DIV1 = 1, DIV2 = 2, DIV3 = 3, DIV4 = 4;

localparam DIV5 = 5, DIV6 = 6, DIV7 = 7, DIV8 = 8;

reg [7:0] fre_en;

always@(posedge clk or negedge rst_n) begin

if(~rst_n) begin

fre_en <= 0;

end

else begin

case(div)

DIV1: fre_en <= 8'b0000_0001;

DIV2: fre_en <= 8'b0000_0010;

DIV3: fre_en <= 8'b0000_0100;

DIV4: fre_en <= 8'b0000_1000;

DIV5: fre_en <= 8'b0001_0000;

DIV6: fre_en <= 8'b0010_0000;

DIV7: fre_en <= 8'b0100_0000;

DIV8: fre_en <= 8'b1000_0000;

endcase

end

end

reg clk_out_r;

reg clk_even;

always @(posedge clk or negedge rst_n) begin

if(~rst_n) begin

clk_even <= 0;

clk_out_r <= 0;

end

else begin

case(1'b1)

fre_en[0]: begin //1 分频

;

end

fre_en[1]: begin //2 分频

clk_even <= ~clk_even;

end

fre_en[2]: begin //3 分频

if(fre_cnt == 1) clk_out_r <= ~clk_out_r;

else if(fre_cnt == 2) clk_out_r <= ~clk_out_r;

else clk_out_r <= clk_out_r;

end

fre_en[3]: begin //4 分频

if(fre_cnt == 1) clk_even <= ~clk_even;

else if(fre_cnt ==3) clk_even <= ~clk_even;

else clk_even <= clk_even;

end

fre_en[4]: begin //5分频

if(fre_cnt == 2) clk_out_r <= ~clk_out_r;

else if(fre_cnt == 4) clk_out_r <= ~clk_out_r;

else clk_out_r <= clk_out_r;

end

fre_en[5]: begin // 6分频

if(fre_cnt == 2) clk_even <= ~clk_even;

else if(fre_cnt == 5) clk_even <= ~clk_even;

else clk_even <= clk_even;

end

fre_en[6]: begin //7 分频

if(fre_cnt == 3) clk_out_r <= ~clk_out_r;

else if(fre_cnt == 6) clk_out_r <= ~clk_out_r;

else clk_out_r <= clk_out_r;

end

fre_en[7]: begin //8 分频

if(fre_cnt == 3) clk_even <= ~clk_even;

else if(fre_cnt == 7) clk_even <= ~clk_even;

else clk_even <= clk_even;

end

endcase

end

end

//下降沿采样模块

reg clk_out_rr;

always @(negedge clk or negedge rst_n) begin

if(~rst_n) begin

clk_out_rr <= 0;

end

else begin

case(1'b1)

fre_en[0]: ;

fre_en[1]: ;

fre_en[2]: begin

clk_out_rr <= clk_out_r;

end

fre_en[3]: ;

fre_en[4]: begin

clk_out_rr <= clk_out_r;

end

fre_en[5]: ;

fre_en[6]: begin

clk_out_rr <= clk_out_r;

end

fre_en[7]: ;

endcase

end

end

//产生分频时钟

assign clk_out = fre_en[0] ? clk :( ( fre_en[1] | fre_en[3] | fre_en[5] | fre_en[7] )? clk_even : clk_out_r | clk_out_rr );

//计数模块

reg [3:0] fre_cnt;

always @(posedge clk or negedge rst_n) begin

if(~rst_n) begin

fre_cnt <= 0;

end

else begin

case(1'b1)

fre_en[0]: begin

;

end

fre_en[1]: begin

;

end

fre_en[2]: begin //3分频计数

if(fre_cnt < 2) fre_cnt <= fre_cnt + 1;

else fre_cnt <= 0;

end

fre_en[3]: begin

if(fre_cnt < 3) fre_cnt <= fre_cnt + 1;

else fre_cnt <= 0;

end

fre_en[4]: begin

if(fre_cnt < 4) fre_cnt <= fre_cnt + 1;

else fre_cnt <= 0;

end

fre_en[5]: begin

if(fre_cnt < 5) fre_cnt <= fre_cnt + 1;

else fre_cnt <= 0;

end

fre_en[6]: begin

if(fre_cnt < 6) fre_cnt <= fre_cnt + 1;

else fre_cnt <= 0;

end

fre_en[7]: begin

if(fre_cnt < 7) fre_cnt <= fre_cnt + 1;

else fre_cnt <= 0;

end

endcase

end

end

endmodule

测试:

module Sim_Freq_Div(

);

reg clk;

reg rst_n;

reg [3:0] div;

wire clk_out;

initial begin

clk = 0;

forever

#2 clk = ~clk;

end

initial begin

rst_n = 0;

div = 5;

#15

rst_n = 1;

#60

div = 1;

end

Fre_Div fre_div_tb(

.clk(clk),

.rst_n(rst_n),

.div(div),

.clk_out(clk_out)

);

endmodule

文章转载自: FPGA技术联盟

最新文章

最新文章