各大公司IC面试手撕代码总结(超全面)

嵌入式ARM 2023-01-31 12:02

1. 用任意语言写1:100的质数

  • C语言
// FileName: HelloWorld.java
public class HelloWorld {
  // Java 入口程序,程序从此入口
  public static void main(String[] args) {
    System.out.println("Hello,World!"); // 向控制台打印一条语句
  }
}

支持以下语言种类:

#include
//输出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测试点和设计

  1. 同时读写,读写数据正确检查
  2. FIFO满标志位检查
  3. FIFO空标志位检查
  4. 写过程中发生写复位,写数据和FIFO满标志位被清空
  5. 读过程中发生读复位,读数据和读空标志位被清空
  6. 读写时钟相位相同,异步FIFO能正常工作
  7. 读写时钟相位不同,异步FIFO能正常工作
  8. 写时钟等与读时钟,异步FIFO能正常工作
  9. 写时钟快于读时钟,异步FIFO能正常工作
  10. 写时钟慢于读时种异步FIFO能正常工作
  11. 写过程中发生写复位以后,异步FIFO能继续正常工作
  12. 读过程中发生读复位以后,异步FIFO能继续正常工作
  13. 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

上边是我自己敲得啦,如果有错误的地方请指正!

END

来源:酒酒聊IC编程

版权归原作者所有,如有侵权,请联系删除。

推荐阅读
看完这些C语言例子,你一定连说5个卧槽!
一款活跃了17年的开发工具,好用到起飞~
最近爆火的电子血氧仪是什么原理?测的准吗?

→点关注,不迷路←

嵌入式ARM 关注这个时代最火的嵌入式ARM,你想知道的都在这里。
评论 (0)
我要评论
0
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦