持续更新中....
以下均为秋招做过的真题。
因为一些众所周知的原因,就不透露是具体是哪些公司的题了
祝大家都收到心仪的offer!
一、手撕
1.全加器
半加器:只有两个加数,无进位;S = A ^ B; Cout=AB;
全加器:S = A^B^C; Cout = AB + Cin(A^B)
求和位:奇数个1,求和为1,所以是异或
进位:两个加数都为1;进位为1,一个以上的加数为1;
组合逻辑写法:
module full_adder_combinational (
input [3:0] A,
input [3:0] B,
input cin,
output [3:0] sum,
output cout
);
wire [3:0] carry;
// 第一位全加器
assign sum[0] = A[0] ^ B[0] ^ cin;
assign carry[0] = (A[0] & B[0]) | (A[0] & cin) | (B[0] & cin);
// 第二位全加器
assign sum[1] = A[1] ^ B[1] ^ carry[0];
assign carry[1] = (A[1] & B[1]) | (A[1] & carry[0]) | (B[1] & carry[0]);
// 第三位全加器
assign sum[2] = A[2] ^ B[2] ^ carry[1];
assign carry[2] = (A[2] & B[2]) | (A[2] & carry[1]) | (B[2] & carry[1]);
// 第四位全加器
assign sum[3] = A[3] ^ B[3] ^ carry[2];
assign carry[3] = (A[3] & B[3]) | (A[3] & carry[2]) | (B[3] & carry[2]);
assign cout = carry[3];
endmodule
时序逻辑写法
module full_adder_sequential (
input clk,
input rst,
input [3:0] A,
input [3:0] B,
input cin,
output reg [3:0] sum,
output reg cout
);
reg [3:0] A_reg, B_reg;
reg cin_reg;
reg [3:0] carry;
always @(posedge clk or posedge rst) begin
if (rst) begin
A_reg <= 4'b0;
B_reg <= 4'b0;
cin_reg <= 1'b0;
sum <= 4'b0;
cout <= 1'b0;
carry <= 4'b0;
end else begin
// 锁存输入
A_reg <= A;
B_reg <= B;
cin_reg <= cin;
// 计算每一位的和与进位
sum[0] <= A_reg[0] ^ B_reg[0] ^ cin_reg;
carry[0] <= (A_reg[0] & B_reg[0]) | (A_reg[0] & cin_reg) | (B_reg[0] & cin_reg);
sum[1] <= A_reg[1] ^ B_reg[1] ^ carry[0];
carry[1] <= (A_reg[1] & B_reg[1]) | (A_reg[1] & carry[0]) | (B_reg[1] & carry[0]);
sum[2] <= A_reg[2] ^ B_reg[2] ^ carry[1];
carry[2] <= (A_reg[2] & B_reg[2]) | (A_reg[2] & carry[1]) | (B_reg[2] & carry[1]);
sum[3] <= A_reg[3] ^ B_reg[3] ^ carry[2];
carry[3] <= (A_reg[3] & B_reg[3]) | (A_reg[3] & carry[2]) | (B_reg[3] & carry[2]);
cout <= carry[3];
end
end
endmodule
2.奇数分频器
5分频,占空比50%,依然计数5,两个高电平。
module exercise(
input clk, rst,
output wire clk5
);
reg [2:0] cnt1,cnt2;
reg q0,q1;
always@(posedge clk or negedge rst)
if(!rst) cnt1 <= 0;
else if(cnt1 == 3'd4) cnt1 <= 0;
else cnt1 <= cnt1 + 1;
always@(negedge clk or negedge rst)
if(!rst) cnt2 <= 0;
else if(cnt2 == 3'd4) cnt2 <= 0;
else cnt2 <= cnt2 + 1;
always@(posedge clk or negedge rst)
if(!rst) q0 <= 0;
else if((cnt1 == 0) || (cnt1 == 3'd2 )) q0 <= ~q0;
else q0 <= q0;
always@(negedge clk or negedge rst)
if(!rst) q1 <= 0;
else if((cnt2 == 0) || (cnt2 == 3'd2 )) q1 <= ~q1;
else q1 <= q1;
assign clk5 = q0 | q1;
endmodule
使用DC命令定义时钟,并约束关联时钟之间的关系
3.双边沿检测电路
module dual_edge_detector (
input wire clk,
input wire rst_n,
input wire signal, // 输入信号
output reg any_edge // 双边沿检测输出
);
reg signal_dly; // 输入信号延迟一拍
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
signal_dly <= 1'b0;
any_edge <= 1'b0;
end else begin
signal_dly <= signal; // 缓存上一拍的值
any_edge <= signal ^ signal_dly; // 检测任何变化
end
end
endmodule
4.求输入序列中最大的两个值
module top2_sort
#(
parameter DWIDTH = 8 // 数据位宽参数
)
(
input clk , // 时钟
input rst_n , // 异步复位(低有效)
input srst , // 同步复位
input [DWIDTH -1:0] din , // 串行输入数据
input din_vld, // 输入数据有效信号
output reg [DWIDTH -1:0] dout_top1, // 输出最大值
output reg [DWIDTH -1:0] dout_top2, // 输出次大值
output reg dout_vld // 输出有效信号
);
// 时序逻辑:在时钟上升沿、复位时更新最大/次大值
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
dout_top1 <= {DWIDTH{1'b0}};
dout_top2 <= {DWIDTH{1'b0}};
dout_vld <= 1'b0;
end else if (srst) begin // 同步复位:同样初始化
dout_top1 <= {DWIDTH{1'b0}};
dout_top2 <= {DWIDTH{1'b0}};
dout_vld <= 1'b0;
end else begin
if (din_vld) begin // 输入数据有效时,进行最大、次大值的更新
if (din > dout_top1) begin // 新数据比当前最大值还大
dout_top2 <= dout_top1; // 原最大值降级为“次大值”
dout_top1 <= din; // 新数据成为“最大值”
end else if (din > dout_top2) begin // 新数据小于最大值、但大于次大值
dout_top2 <= din; // 新数据成为“次大值”
end
dout_vld <= 1'b1;
end
end
end
endmodule
二、时序
三、问答
说明同步复位,异步复位,同步复位异步释放的实现方法以及各自的优缺点。
- 同步复位:复位信号只在时钟的有效边沿(上升沿或下降沿)起作用,即只有时钟边沿来临时,才会检测复位信号。优点是抗干扰能力强,可靠性高,利于静态时序分析;缺点复位必须等待时钟有效沿才能起作用,所以复位信号可能较长。、
- 异步复位:复位信号一旦有效,会立即生效,无需等待时钟边沿。优点是响应快,但缺点是复位信号的不正确释放可能会导致亚稳态问题。
- 异步复位,同步释放:复位信号是异步有效的(立即生效),但复位释放是同步的(避免亚稳态)。优点是响应快,且由于是同步释放,即在复位结束后能更稳定的进入工作状态,电路更稳定。缺点是设计更复杂,增加设计和验证的工作量。(同步释放需要多一个always块来给复位信号进行至少1级打拍,输出和时钟同步后的复位信号供后面的电路使用)
异步释放,同步复位
module reset_sync (
input wire clk,
input wire async_rst_n, // 来自外部的异步复位
output wire sync_rst_n // 同步化后的复位信号
);
reg rst_n_meta, rst_n_sync;
always @(posedge clk or negedge async_rst_n) begin
if (!async_rst_n) begin
// 异步复位有效时,两级寄存器都清零
rst_n_meta <= 1'b0;
rst_n_sync <= 1'b0;
end else begin
// 异步复位释放后,通过两级DFF同步
rst_n_meta <= 1'b1;
rst_n_sync <= rst_n_meta;
end
end
assign sync_rst_n = rst_n_sync; // 将同步化后的复位信号输出
endmodule
低电平复位的意思:在低电平时系统在进行复位;虽然写成@negedge rstn,看似是检测到下降沿系统复位,但实际是等到rstn变成低电平后系统才开始复位。