酒酒爆肝IC面试手撕代码常考题
哈喽,大家好,我是酒酒,酒酒自学互联网和IC,并拿到20个offer及50W+offer,由于关注酒酒公众号的有CS和IC的,所以本篇是IC的~
1. 用任意语言写1:100的质数
- C语言
// FileName: HelloWorld.java
public class HelloWorld {
// Java 入口程序,程序从此入口
public static void main(String[] args) {
System.out.println("Hello,World!"); // 向控制台打印一条语句
}
}
支持以下语言种类:
#include<stdio.h>
//输出100之内的所有素数
int main()
{
int i;
int j;
int flag = 1;
for(i = 2;i <= 100;i++)
{
for(j = 2;j <= i / 2;j++)
{
if(i % j ==0)
{
flag = 0;
break;
}
}
if(flag == 1)
{
printf("%d\n",i);
}
flag = 1;
}
return 0;
}
- matlab
for i = 2:100 %外层循环,i的初值是2,终值为100
for j = 2:100 %内层循环,j的初值为2,终值为100
if (~mod(i,j)) %i除以j取余后再取反
break; %跳出循环
end
end
if(j > (i/j))
fprintf('%d is prime \n',i);
end
end
2.奇数分频
奇数分频:分别写两个计数器和分频器(分别是上升沿计数、下降沿计数,上升沿分频、下降沿分频),分频器在(N-1)/2和N-1的时候翻转,最后再将两个分频器或
三分频代码
module divtest(
input clk,
input rst_n,
output clk_divider
);
reg [2:0] count_p;
reg [2:0] count_n;
reg clk_p;
reg clk_n;
parameter N = 3;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
count_p <= 3'b0;
else if(count_p == N-1)
count_p <= 3'b0;
else
count_p <= count_p + 1'b1;
end
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
clk_p <= 1'b1;
end
else begin
if(count_p == (N-1)/2) || (count_p == N-1)begin
clk_p <= ~clk_p;
end
end
end
//下降沿计数
always@(negedge clk or negedge rst_n)begin
if(!rst_n)
count_n <= 1'b0;
else if(count_n == N-1)
count_n <= 1'b0;
else
count_n <= count_n + 1'b1;
end
//下降沿分频
always@(negedge clk or negedge rst_n)begin
if(!rst_n)begin
clk_n <= 1'b1;
end
else begin
if(count_n == (N-1)/2 || count_n == N-1)begin
clk_n = ~clk_n;
end
end
end
assign clk_divider = clk_n | clk_p;
endmodule
3.偶数分频
首先定义参数,N分频就是N,偶数分频的时候写一个N位的计数器,再写一个分频器,在N/2-1和N-1的时候翻转
八分频代码
//写一个八分频代码
module divider8(
input clk,
input rst_n,
output dividerclk
);
reg [2:0] count_n;
parameter N = 8;
//写一个计数器
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
count_n <= 1'b0;
else if(count_n == N-1)
count_n <= 1'b0;
else
count_n <= count_n + 1'b1;
end
//写一个分频器
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
dividerclk <= 1'b1;
end
else if(count_n == N/2-1) | (count_n == N-1)begin
dividerclk <= ~dividerclk;
end
end
endmodule
4. 序列检测器,周期性的输出一个序列为0010110111
module xlgen(
input clk,
input rst_n,
output Q
);
reg Q;
reg [9:0] Q_r;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
Q <= 1'b0;
Q_r <= 10'b0010110111;
end
else begin
Q <= Q_r[9];
Q_r <= Q_r1<<1;
Q_r[0] <= Q;
end
end
endmodule
5.异步复位,同步释放
什么情况下,复位信号需要做"异步复位,同步释放"处理?
一般来说,同步系统,都使用异步复位。这是因为同步复位比异步复位的电路实现要浪费更多电路资源。
异步复位,同步释放原理图
//异步复位同步释放
module rstsync(
input clk,
input rst_async_n,
output rst_sync_n
);
reg rst_s1;
reg rst_s2;
always@(posedge clk or negedge rst_async_n)begin
if(!rst_async_n)begin
rst_s1 <= 1'b0;
rst_s2 <= 1'b0;
end
else begin
rst_s1 <= 1'b1;
rst_s2 <= reg_s1;
end
end
assign rst_sync_n = rst_s2;
endmodule
6.用verilog实现1bit信号边沿检测功能,输出一个周期宽度的脉冲信号
重点在上升沿,下降沿,上升沿或下降沿
module EdgeDetect(
input clk,
input rst_n,
input data,
output pos_edge,
output neg_edge,
output data_edge
);
reg [1:0] data_r;
always@(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
data_r <= 2'b00;
end
else begin
data_r <= {data_r[0],data};
end
end
assign pos_edge = ~data_r[1] & data_r[0]; //01
assign neg_edge = data_r[1] & ~data_r[0]; //10
assign data_edge = pos_edge | neg_edge;
endmodule
7. 描述带进位输入、输出的两位全加器
先写一位全加器,然后再写个顶层模块,调用两个一位全加器,其中第一个加法器的cout作为第二个加法器的cin
S = a^b^cin;
Cout = a&b|a&cin|b&cin;
//一位全加器
module fulladder(
input a,
input b,
input cin,
output s,
output cout
);
assign s = a^b^cin;
assign cout = a&b|a&cin|b&cin;
endmodule
//2位全加器
module top(a,b,cin,s,cout);
input [1:0] a;
input [1:0] b;
input cin;
output [1:0] s;
output cout;
wire carry;
//采用结构化描述的方式实现一个2位加法器
fulladder m0(a[0],b[0],cin,s[0],carry);
fulladder m1(a[1],b[1],carry,s[1],cout);
endmodule
8. verilog实现8位串行数据转并行数据
module deserialize(
input rst,clk,din,
output reg [7:0] dout
);
reg [7:0] data;
reg [2:0] cnt;
always@(posedge clk or negedge rst)begin
if(rst == 1'b0)begin
data <= 8'b0;
cnt <= 3'b0;
dout <= 8'b0;
end
else
begin
if(cnt == 3'b111)
begin
dout[7-cnt] <= din;
dout[7:1] <= data[7:1];
cnt <= 3'b0;
end
else
begin
data[7-cnt] <= din;
cnt <= cnt+1;
end
end
end
endmodule
9.跨时钟域(面试经常问!)
单bit(慢时钟域到快时钟域):用快时钟打两拍,直接采一拍大概率也是没问题的,两拍的主要目的是消除亚稳态;单bit(快时钟域到慢时钟域):握手、异步FIFO、异步双口RAM;快时钟域的信号脉宽较窄,慢时钟域不一定能采到,可以通过握手机制让窄脉冲展宽,慢时钟域采集到信号后再“告诉”快时钟域已经采集到信号,确保能采集到;多bit:异步FIFO、异步双口RAM、握手、格雷码;
9.1 慢时钟域信号到快时钟域信号,一般情况下都是打两拍(单比特传输)也叫两级寄存器同步
module clockdomain(
input clk,
input rst_n,
input signal_in,
output signal_out
);
reg [1:0] signal_r;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
signal_r <= 2'b00;
end
else begin
signal_r <= {signal_r[0],signal_in};
end
end
assign signal_out = signal_r[1];
endmodule
9.2快时钟域到慢时钟域(clka慢时钟域,clkb快时钟域)
- 将快的时钟域中的信号进行展宽
- 对展宽的信号在慢的时钟域2级同步
- 在慢时钟域中产生反馈信号来对快的展宽信号进行拉低处理
跨时钟域处理从快时钟域到慢时钟域,如果是下面第一个图,cklb则可以采样到signal_a_in,但是如果 只有单脉冲,如第二个图,则不能确保采样掉signal_a_in。这个时候用两级触发器同步是没有用的。
module Sync_Pulse(
input clka,
input clkb,
input rst_n,
input pulse_ina,
output pulse_outb,
output signal_outb
);
//-------------------------------------------------------
reg signal_a;
reg signal_b;
reg [1:0] signal_b_r;
reg [1:0] signal_a_r;
//-------------------------------------------------------
//在clka下,生成展宽信号signal_a
always @(posedge clka or negedge rst_n)begin
if(rst_n == 1'b0)begin
signal_a <= 1'b0;
end
else if(pulse_ina == 1'b1)begin
signal_a <= 1'b1;
end
else if(signal_a_r[1] == 1'b1)
signal_a <= 1'b0;
else
signal_a <= signal_a;
end
//-------------------------------------------------------
//在clkb下同步signal_a
always @(posedge clkb or negedge rst_n)begin
if(rst_n == 1'b0)begin
signal_b <= 1'b0;
end
else begin
signal_b <= signal_a;
end
end
//-------------------------------------------------------
//在clkb下生成脉冲信号和输出信号
always @(posedge clkb or negedge rst_n)begin
if(rst_n == 1'b0)begin
signal_b_r <= 2'b00;
end
else begin
signal_b_r <= {signal_b_r[0], signal_b};
end
end
assign pulse_outb = ~signal_b_r[1] & signal_b_r[0];
assign signal_outb = signal_b_r[1];
//-------------------------------------------------------
//在clka下采集signal_b[1],生成signal_a_r[1]用于反馈拉低signal_a
always @(posedge clka or negedge rst_n)begin
if(rst_n == 1'b0)begin
signal_a_r <= 2'b00;
end
else begin
signal_a_r <= {signal_a_r[0], signal_b_r[1]};
end
end
endmodule
10.写一个同步FIFO,深度为16,数据位宽为8bit
module Syn_fifo
#(
parameter DATA_WIDTH = 8,
parameter ADDR_WIDTH = 4,
parameter RAM_DEPTH = (1 << ADDR_WIDTH)
)
(
input clk,
input rst_n,
input [DATA_WIDTH-1:0] data_in,
input wr_en,
input rd_en,
output reg [DATA_WIDTH-1:0] data_out,
output empty, //fifo empty
output full //fifo full
);
reg [ADDR_WIDTH-1:0] wr_cnt;
reg [ADDR_WIDTH-1:0] rd_cnt;
reg [ADDR_WIDTH-1:0] status_cnt;
reg [DATA_WIDTH-1:0] data_ram;
//-------------------------------------------------------
assign full = (status_cnt == (RAM_DEPTH-1))? 1'b1: 1'b0;
assign empty = (status_cnt == 0)? 1'b1: 1'b0;
//Syn
reg rd_en_r;
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
rd_en_r <= 0;
end
else begin
rd_en_r <= rd_en;
end
end
//-------------------------------------------------------
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
wr_cnt <= 0;
end
else if(wr_cnt == RAM_DEPTH-1)
wr_cnt <= 0;
else if(wr_en)begin
wr_cnt <= wr_cnt + 1'b1;
end
else
wr_cnt <= wr_cnt;
end
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
rd_cnt <= 0;
end
else if(rd_cnt == RAM_DEPTH-1)
rd_cnt <= 0;
else if(rd_en)begin
rd_cnt <= rd_cnt + 1'b1;
end
else
rd_cnt <= rd_cnt;
end
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
data_out <= 0;
end
else if(rd_en_r)begin
data_out <= data_ram;
end
end
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
status_cnt <= 0;
end
else if(rd_en && !wr_en && (status_cnt != 0))begin
status_cnt <= status_cnt - 1;
end
else if(wr_en && !rd_en && (status_cnt != RAM_DEPTH-1))
status_cnt <= status_cnt + 1;
else
status_cnt <= status_cnt;
end
//-------------------------------------------------------
//Syn_Dual_Port_RAM
integer i;
reg [DATA_WIDTH-1:0] register[RAM_DEPTH-1:0];
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
for(i = 0; i < RAM_DEPTH; i = i + 1)
register[i] <= 0;
end
else if(wr_en == 1'b1)
register[wr_cnt] <= data_in;
end
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
data_ram <= 0;
end
else if(rd_en == 1'b1)
data_ram <= register[rd_cnt];
else
data_ram <= data_ram;
end
endmodule
11.异步FIFO测试点和设计
- 同时读写,读写数据正确检查
- FIFO满标志位检查
- FIFO空标志位检查
- 写过程中发生写复位,写数据和FIFO满标志位被清空
- 读过程中发生读复位,读数据和读空标志位被清空
- 读写时钟相位相同,异步FIFO能正常工作
- 读写时钟相位不同,异步FIFO能正常工作
- 写时钟等与读时钟,异步FIFO能正常工作
- 写时钟快于读时钟,异步FIFO能正常工作
- 写时钟慢于读时种异步FIFO能正常工作
- 写过程中发生写复位以后,异步FIFO能继续正常工作
- 读过程中发生读复位以后,异步FIFO能继续正常工作
- FIFO满以后,继续往FIFO写数据,异步FIFO不会被卡死,数据被读走以后,异步步FIFO能继续正常工作。异步FIFO设计:
`timescale 1ns / 1ps
//
// Company:
// Engineer: zhangsy
//
// Create Date: 2019/08/05 11:58:12
// Design Name:
// Module Name: asynfifo
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module asynfifo#(
parameter data_depth = 4,
parameter data_width = 4,
parameter ptr_width = 2
)
(
input w_clk,r_clk,
input write,read,
input rst_n,
input [data_width-1:0] data_in,
output [data_width-1:0] data_out,
output valid,
output full,
output emty
);
///
//name enable
//write H
//read H
//rst_n L
//valid H
//full H
//emty H
/
reg [data_width-1:0]fifo [data_depth-1:0];
reg [ptr_width:0] r_ptr;
wire [ptr_width:0]r_ptr_g;
reg [ptr_width:0] w_ptr;
wire [ptr_width:0] w_ptr_g;
//****************update r_ptr************
always @ (posedge r_clk)
if(!rst_n)
r_ptr <= 2'd0;
else if(read && !emty )
r_ptr <= r_ptr + 1'b1;
assign r_ptr_g = (r_ptr >>1) ^r_ptr;
//****************update w_ptr************
always @ (posedge w_clk)
if(!rst_n)
w_ptr <= 2'd0;
else if(write && !full )
w_ptr <= w_ptr + 1'b1;
assign w_ptr_g = (w_ptr >>1) ^w_ptr;
//---------------------------------read-------------
//******************synchronization w_ptr***********
reg[ptr_width:0] w_reg1,w_reg2;
wire [ptr_width:0] w_gray;
reg [data_width-1:0] dout;
reg valid_r;
always @ (posedge r_clk)
if(!rst_n)
{w_reg1,w_reg2} <= 2'b00;
else
{w_reg1,w_reg2} <= {w_ptr,w_reg1};
always @(posedge r_clk)
if(!rst_n)
begin
dout <= 'h0;
valid_r <= 1'b0;
end
else if(read && !emty)
begin
dout <= fifo[r_ptr[ptr_width-1-:ptr_width]];
valid_r <= 1'b1;
end
else
begin
dout <= 'h0;
valid_r <= 1'b0;
end
//---------------------------------write-------------
//******************synchronization r_ptr***********
reg[ptr_width:0] r_reg1,r_reg2;
wire [ptr_width:0] r_gray;
always @ (posedge w_clk)
if(!rst_n)
{r_reg1,r_reg2} <= 2'b00;
else
{r_reg1,r_reg2} <= {r_ptr,r_reg1};
always @ (posedge w_clk)
if(write && !full)
begin
fifo[w_ptr[ptr_width-1-:ptr_width]] <= data_in;
end
//---------------gray----------
assign r_gray = (r_reg2 >> 1) ^r_reg2;
assign w_gray = (w_reg2 >> 1) ^w_reg2;
assign full = ({~w_ptr_g[ptr_width:ptr_width-1],w_ptr_g[ptr_width-2:0]}== r_gray);
assign emty = (w_gray == r_ptr_g);
assign data_out = dout;
assign valid = valid_r;
endmodule
12.格雷码和二进制的转换
12.1 二进制转格雷码
原数右移一位再与原数异或
module bin_to_gray(
bin_in,
gray_out
);
parameter data_width = 4;
input [data_width-1:0] bin_in;
output [data_width-1:0] gray_out;
assign gray_out = (bin_in >> 1)^bin_in;
endmodule
12.2 格雷码转二进制
二进制最高位取格雷码最高位,然后循环做二进制高位与低一位的格雷码异或
module gray_to_bin(
gray_in,
bin_out
);
parameter data_width = 4;
input [data_width-1:0] gray_in;
output [data_width-1:0] bin_out;
reg [data_width-1:0] bin_out;
always@(gray_in)begin
bin_out[3] = gray_in[3];
bin_out[2] = gray_in[2]^gray_in[3];
bin_out[1] = gray_in[1]^gray_in[2];
bin_out[0] = gray_in[0]^gray_in[1];
end
endmodule
上边是酒酒自己敲得啦,如果有错误的地方请指正哈~
另外最近有很多小伙伴问:酒酒,我研究生,本科是专业,研究生是**,现在研二/研三,想备战明天春招、秋招可以嘛?例如:
主要分为三种情况:参加明年春招,秋招,以及现在才研一,甚至即将步入研究生生活的小伙伴~
- 参加明年春招的小伙伴
- 离明年三四月份春招还剩5个月,如果可以的话,能签个保底的工作可以先签一下,我们你们春招万一没找着合适的,就啥也没了
- 然后需要考虑一下毕业论文的准备情况,毕业要紧,如果毕业论文准备的差不多了,有充足的时间学习,那还是挺不错的
- 由于春招的名额可能较少,可能竞争会激烈一些,所以你可以考虑清楚自己的优势在哪里,学校,专业,学习能力以及面试表现等,对自己有清晰的认知和定位,比如说以你的水平,能不能进一些大厂,万一进不了大厂,小公司是否能够接收,考虑清楚之后再决定,还有就是你为啥要转IC?当然这也是面试中经常会问的问题~
- 参加明年秋招的小伙伴
- 你们的时间还比较充裕,还有一年,这一年中,只要你好好学,有充足的时间的话,问题应该都不太大,只不过明年的形势不好说,是否薪资还这么高我可不敢保证~
- 很多人都问参加明年秋招转IC还来得及嘛,可能你是想听到一个肯定吧,我说不行,你也不会相信,但我感觉重点是你自己内心想不想转?有没有决心,行动力强不强,当然这跟个人因素有很大相关性的
- 还有就是不要以为时间很长,不着急,有时候你会发现早学那么几个月真的不一样的,举个例子,我有个小伙伴去年12月就开始学验证了,他寒假也会学点儿,我记得我过年那会儿刚有想学验证的念头的时候,他都学到UVM的phase机制了,那会儿在寒假我也没啥动力,就看了看入门的几节课,好像连SV的前两章都没看完,开学后,人家做了做虚拟项目就去投实习了,拿了好几家offer,最后去了平头哥实习,而我当时学的很少都没敢投实习,就投了海康,结果性格测评还给挂了
- 目前研一甚至即将步入研究生的小伙伴
- 你们还早,还有两三年呢,先做好当下的事情,提升自己的能力,丰富自己的简历
- 不晓得三年后IC发展会怎么样,这很难说,感觉变化挺大的,三年前我们微电子专业的学生保研很少有人选本专业,好多都去做信号处理了,因此,可以先学点儿IC相关知识,也可以学学JAVA,你的选择有很多,不要太局限
原文链接: https://mp.weixin.qq.com/s/ynL4tFLdek8d6puEHTI2Qw
作者:酒酒
推荐阅读
更多IC设计技术干货请关注IC设计技术专栏。欢迎添加极术小姐姐微信(id:aijishu20)加入技术交流群,请备注研究方向。