刷题:牛客-快速入门篇

前言

正文

牛客刷题链接

一、基础语法

VL4 移位运算与乘法

刷题:牛客-快速入门篇_第1张图片
刷题:牛客-快速入门篇_第2张图片

刷题:牛客-快速入门篇_第3张图片
乘法:将d进行移位相加

//4、移位运算与乘法
`timescale 1ns/1ns
module multi_sel(
	input [7:0]d ,
	input clk,//200MHZ,5ns
	input rst,
	output  reg input_grant,
	output  reg [10:0]out
);

reg [1:0] cnt_3;
always@(posedge clk or negedge rst)begin
	if(!rst)begin
		cnt_3 <= 2'd0;
	end
	else if(cnt_3 == 2'd3)begin
		cnt_3 <= 2'd0;
	end
	else
		cnt_3 <= cnt_3 + 2'd1;
end

reg [7:0] d_reg;

always@(posedge clk or negedge rst)begin
	if(!rst)begin
		input_grant = 1'd0;
		d_reg 		= 8'd0;
		out			= 8'd0;
	end
	else begin
	
	case(cnt_3)
		2'd0:begin
			input_grant = 1'd1;
			d_reg 		= d;//寄存数据
			out			= d;
		end
		2'd1:begin
			input_grant = 1'd0;
			out 		= d_reg + (d_reg<<1'd1);//3
		end
		2'd2:begin
			input_grant = 1'd0;
			out 		= d_reg + (d_reg<<1'd1) + (d_reg<<2'd2) ;//7
		end
		2'd3:begin
			input_grant = 1'd0;
			out 		= (d_reg<<3'd3);//8
		end		
	default:;
	endcase
	end
end
endmodule

VL5 位拆分与运算

刷题:牛客-快速入门篇_第4张图片


`timescale 1ns/1ns

module data_cal(
input clk,
input rst,
input [15:0]d,
input [1:0]sel,

output reg [4:0]out,
output reg validout
);
//*************code***********//

reg [15:0] d_reg;
always@(posedge clk or negedge rst)begin
	if(!rst)begin
		out <= 5'd0;
		validout <= 1'd0;
		d_reg <= 16'd0;
	end
	else
	case(sel)
	2'd0: begin
		validout <= 1'd0;
		d_reg <= d;//锁存数据
		out <= 5'd0;
		end
	2'd1: begin
		validout <= 1'd1;
		out <= d_reg[3:0] + d_reg[7:4];
		end
	2'd2: begin
		validout <= 1'd1;
		out <= d_reg[3:0] + d_reg[11:8];
		end
	2'd3: begin
		validout <= 1'd1;
		out <= d_reg[3:0] + d_reg[15:12];
		end
	default:;
	endcase
end


//*************code***********//
endmodule

刷题:牛客-快速入门篇_第5张图片

VL6 多功能数据处理器

根据指示信号select的不同,对输入信号a,b实现不同的运算。输入信号a、b为8bit有符号数,

当select[1;0] =0,输出a;
当select[1;0] =1,输出b;
当select[1;0] =2,输出a+b;
当select[1;0] =3,输出a-b。
因为输入输出都已经直接定义了signed有符号数类型,所以直接相加、相减也没有问题,不会出现运算错误。

有符号数+有符号数=有符号数,但是如果表达式中有一个无符号数,则所有的操作数都会被强行转换为无符号数
两种解决方法

(1)涉及到有符号数运算时,和有符号相关的输入、输出、中间变量均定义成signed有符号数,这样全部遵循有符号数运算规则;

(2)用位拼接符补齐符号位;

// VL6 多功能数据处理器
// 1.有符号数的减法运算
// 2.选择器
// 3.不同位宽赋值
`timescale 1ns/1ns
module data_select(
	input clk,
	input rst_n,
	input signed[7:0]a,
	input signed[7:0]b,
	input [1:0]select,
	
	output reg signed [8:0]c
);
always@(posedge clk or negedge rst_n)begin
	if(!rst_n )begin
		c <= 9'd0;
	end
	case(select)
		2'd0: c <= a;
		2'd1: c <= b;
		2'd2: c <= a + b;
		2'd3: c <= a - b;
	default:;
	endcase
end

endmodule

VL8 使用generate…for语句简化代码

参考笔记,总结详细

作用上

和for是一样的;

区别

(1)generate for的循环变量必须用genvar声明,for的变量可以用reg、integer整数等多种类型声明;

(2)for只能用在always块里面,generate for可以做assign赋值,用always块话always写在generate for里;

(3)generate for后面必须给这个循环起一个名字,for不需要;

(4)generate for还可以用于例化模块
格式
genvar i;
generate
for(i=0;表达式2;表达式3)
begin :起个名字
…;
end
endgenerate

// VL8 使用generate…for语句简化代码
`timescale 1ns/1ns
module gen_for_module( 
    input [7:0] data_in,
    output [7:0] data_out
);

genvar i;
generate

for (i = 0; i < 8 ; i=i+1)
begin: myloop
	assign data_out[i] = data_in[7-i];

end

endgenerate

endmodule

等价于
因此for循环里的语句也是并行的


module template_module( 
    input [7:0] data_in,
    output [7:0] data_out
);
    assign data_out [0] = data_in [7];
    assign data_out [1] = data_in [6];
    assign data_out [2] = data_in [5];
    assign data_out [3] = data_in [4];
    assign data_out [4] = data_in [3];
    assign data_out [5] = data_in [2];
    assign data_out [6] = data_in [1];
    assign data_out [7] = data_in [0];
    
endmodule

VL9 使用子模块实现三输入数的大小比较

刷题:牛客-快速入门篇_第6张图片

`timescale 1ns/1ns
module main_mod(
	input clk,
	input rst_n,
	input [7:0]a,
	input [7:0]b,
	input [7:0]c,
	
	output [7:0]d
);
wir [7:0] min_ab;
wir [7:0] min_ac;
wir [7:0] min_abc;

sub_mod sub_mod_inst_01(
	.clk(clk),
	.rst_n(rst_n),
	.a(a),
	.b(b),
	
	.d(min_ab)
);

sub_mod sub_mod_inst_02(
	.clk(clk),
	.rst_n(rst_n),
	.a(a),
	.b(c),
	
	.d(min_ac)
);

sub_mod sub_mod_inst_03(
	.clk(clk),
	.rst_n(rst_n),
	.a(min_ab),
	.b(min_ac),
	
	.d(min_abc)
);

assign d = min_abc;
endmodule

//子模块
module sub_mod(
	input clk,
	input rst_n,
	input [7:0]a,
	input [7:0]b,
	
	output reg[7:0]d
);
always@(posedge clk or negedge rst_n)begin
	if(!rst_n )begin
		d <= 8'd0;
	end
	else if(a>b)begin
		d <= b;
	end
	else if(a<b)
		d <= a;
	else
		d <= d;
end

endmodule

VL10 使用函数实现数据大小端转换

function笔记
实现向量位转换
HDLBits类似题

`timescale 1ns/1ns
module function_mod(
	input clk,
	input rst_n,
	input [3:0]a,
	input [3:0]b,
	
	output  [3:0]c,
	output  [3:0]d
);

assign c = rst_n ? change_bit(a) : 1'd0;
assign d = rst_n ? change_bit(b) : 1'd0;

function [3:0]change_bit;
input [3:0]data_in;

integer i;
for (i = 0; i < 4 ; i=i+1)
begin: myloop
    change_bit[i] = data_in[3-i];
end

endfunction

endmodule

二、组合逻辑

VL11 4位数值比较器电路

题目中规定了要用门级描述:参考答案
刷题:牛客-快速入门篇_第7张图片
首先根据真值表写出表达式
然后画出对应的电路图
刷题:牛客-快速入门篇_第8张图片
最后可以用Verilog描述出上面的电路
刷题:牛客-快速入门篇_第9张图片

// VL11 4位数值比较器电路
`timescale 1ns/1ns

module comparator_4(
	input		[3:0]       A   	,
	input	   [3:0]		B   	,
 
 	output	 wire		Y2    , //A>B
	output   wire        Y1    , //A=B
    output   wire        Y0      //A
);
// 方法一:使用比较大小的方法,虽然能运行出来,但是题目要求是欧诺个门级描述
// 		   因此最好不适用比较大小的方法
/* assign Y2 = ((A[3]>B[3])||(A[2]>B[2])||((A[1]>B[1]) && (A[2]==B[2]) && (A[3]==B[3]))||((A[0]>B[0])&&(A[2]==B[2])&&(A[1]==B[1]) && (A[3]==B[3])))?1'd1:1'd0;
assign Y0 = ((A[3]


//方法二

not iv0(iv0_o, B[0]),
    iv1(iv1_o, B[1]),
    iv2(iv2_o, B[2]),
    iv3(iv3_o, B[3]),
    iv4(iv4_o, A[0]),
    iv5(iv5_o, A[1]),
    iv6(iv6_o, A[2]),
    iv7(iv7_o, A[3]);
    
and ad0(
, iv0_o, A[0]),
    ad1(ad1_o, iv1_o, A[1]),
    ad2(ad2_o, iv2_o, A[2]),
    ad3(ad3_o, iv3_o, A[3]),
    
    ad4(ad4_o, ad0_o, xnr0_o, xnr1_o, xnr2_o),
    ad5(ad5_o, ad1_o, xnr1_o, xnr2_o),
    ad6(ad6_o, ad2_o, xnr2_o),
    
    ad7(ad7_o, iv4_o, B[0]),
    ad8(ad8_o, iv5_o, B[1]),
    ad9(ad9_o, iv6_o, B[2]),
    ad10(ad10_o, iv7_o, B[3]),
    
    ad11(ad11_o, ad7_o, xnr0_o, xnr1_o, xnr2_o),
    ad12(ad12_o, ad8_o, xnr1_o, xnr2_o),
    ad13(ad13_o, ad9_o, xnr2_o),
    ad14(Y1, xnr2_o, xnr1_o, xnr0_o, xnr3_o);
    
xnor xnr0(xnr0_o, A[1], B[1]),  
     xnr1(xnr1_o, A[2], B[2]),   
     xnr2(xnr2_o, A[3], B[3]),   
     xnr3(xnr3_o, A[0], B[0]);  

or or0(Y2, ad3_o, ad6_o, ad5_o, ad4_o),
   or1(Y0, ad10_o, ad13_o, ad12_o, ad11_o) ;    
    


endmodule

VL13 优先编码器电路①

编码器:输入多个0、1编码信号,输出编码后的二进制
普通和优先
1. 普通:输入只有1个编码信号
2. 优先:输入只有多个编码信号

`timescale 1ns/1ns
module encoder_0(
   input      [8:0]         I_n   ,
    
   output reg [3:0]         Y_n  
);
    always@(*) begin
        casez (I_n)
            9'b1_1111_1111: Y_n = 4'b1111;
            9'b0_????_????: Y_n = 4'b0110;
            9'b1_0???_????: Y_n = 4'b0111;
            9'b1_10??_????: Y_n = 4'b1000;
            9'b1_110?_????: Y_n = 4'b1001;
            9'b1_1110_????: Y_n = 4'b1010;
            9'b1_1111_0???: Y_n = 4'b1011;
            9'b1_1111_10??: Y_n = 4'b1100;
            9'b1_1111_110?: Y_n = 4'b1101;
            9'b1_1111_1110: Y_n = 4'b1110;
            default: Y_n = 4'b0000;
        endcase
    end
endmodule

VL14 用优先编码器①实现键盘编码电路

10个按键分别对应十进制数0-9,按键9的优先级别最高;按键悬空时,按键输出高电平,按键按下时,按键输出低电平;键盘编码电路的输出是8421BCD码。

要求:键盘编码电路要有工作状态标志,以区分没有按键按下和按键0按下两种情况

思路

  1. 10路输入编码信号,输出用8421BCD表示
  2. 输入信号是低电平有效,且有优先级——用casez
//VL14 用优先编码器①实现键盘编码电路
`timescale 1ns/1ns

module key_encoder(
      input      [9:0]         S_n   ,         
 
      output reg[3:0]         L     ,
      output wire              GS
);
	always@(*)begin
		casez(S_n)
			10'b0????_?????:  L = 4'b1001;
			10'b10???_?????:  L = 4'b1000;
			10'b110??_?????:  L = 4'b0111;
			10'b1110?_?????:  L = 4'b0110;
			10'b11110_?????:  L = 4'b0101;
				
			10'b11111_0????:  L = 4'b0100;
			10'b11111_10???:  L = 4'b0011;
			10'b11111_110??:  L = 4'b0010;
			10'b11111_1110?:  L = 4'b0001;
			10'b11111_11110:  L = 4'b0000;
		default:L = 4'b0000;
		endcase
	end
	assign GS = (S_n == 10'b11111_11111)?1'd1:1'd0;

endmodule

VL15 优先编码器Ⅰ

和数电课本上的差不多,只是没有对信号进行取反 数电第六版闫石P(155页)

//VL15 优先编码器Ⅰ
`timescale 1ns/1ns

module encoder_83(
   input      [7:0]       I   ,
   input                  EI  ,
   
   output reg [2:0]       Y   ,
   output wire            GS  ,
   output wire            EO    
);
	always@(*)begin
	case(EI)
		1'd0:
			Y = 3'b000;
		1'd1:
			casez(I)
				8'b0000_0000: Y = 3'b000;
				8'b1???_????: Y = 3'b111;
				8'b01??_????: Y = 3'b110;
				8'b001?_????: Y = 3'b101;
				8'b0001_????: Y = 3'b100;
				8'b0000_1???: Y = 3'b011;
				8'b0000_01??: Y = 3'b010;
				8'b0000_001?: Y = 3'b001;
				8'b0000_0001: Y = 3'b000;
			default:Y = 3'b000;
			endcase
	endcase
	end
	
	assign EO = EI&~I[7]&~I[6]&~I[5]&~I[4]&~I[3]&~I[2]&~I[1]&~I[0];
 
	assign GS = EI&(I[7] | I[6] | I[5] | I[4] | I[3] | I[2] | I[1] | I[0]);

/* 	assign GS =(I[0]) & EI;
	assign EO =(yu) & EI;
 */
/* 	reg [7:0]add = 'd0;
	genvar i;
	generate
		for(i = 0 ; i<10 ; i = i+1)begin: add_I
			add = add + I[i];
		end

	endgenerate

	reg [7:0]yu = 'd0;
	genvar j;
	generate
		for(j = 0 ; j<10 ; j = j+1)begin: yu_I
			yu = yu & (~I[j]);
		end

	endgenerate */


endmodule

VL16 使用8线-3线优先编码器Ⅰ实现16线-4线优先编码器

刷题:牛客-快速入门篇_第10张图片

//VL16 使用8线-3线优先编码器Ⅰ实现16线-4线优先编码器
`timescale 1ns/1ns

module encoder_83(
   input      [7:0]       I   ,
   input                  EI  ,
   
   output wire [2:0]      Y   ,
   output wire            GS  ,
   output wire            EO    
);
assign Y[2] = EI & (I[7] | I[6] | I[5] | I[4]);
assign Y[1] = EI & (I[7] | I[6] | ~I[5]&~I[4]&I[3] | ~I[5]&~I[4]&I[2]);
assign Y[0] = EI & (I[7] | ~I[6]&I[5] | ~I[6]&~I[4]&I[3] | ~I[6]&~I[4]&~I[2]&I[1]);

assign EO = EI&~I[7]&~I[6]&~I[5]&~I[4]&~I[3]&~I[2]&~I[1]&~I[0];

assign GS = EI&(I[7] | I[6] | I[5] | I[4] | I[3] | I[2] | I[1] | I[0]);
//assign GS = EI&(| I);
         
endmodule


module encoder_164(
   input      [15:0]      A   ,
   input                  EI  ,
   
   output wire [3:0]      L   ,
   output wire            GS  ,
   output wire            EO    
);

wire       EO1 ;
wire [2:0] Y0  ; 
wire [2:0] Y1  ;
wire       GS0 ; 
encoder_83 U0(
   .I   (A[7:0]),
   .EI  (EO1   ),
   
   .Y   (Y0    ),
   .GS  (GS0   ),
   .EO  (EO    )  
);
encoder_83 U1(
   .I   (A[15:8]),
   .EI  (EI     ),
   
   .Y   (Y1    ),
   .GS  (GS1   ),
   .EO  (EO1   )  
);

assign L[3] = GS1;
assign L[2] = Y1[2] | Y0[2];
assign L[1] = Y1[1] | Y0[1];
assign L[0] = Y1[0] | Y0[0];

assign GS = GS1 | GS0;

         
endmodule

三、时序逻辑

VL21 根据状态转移表实现时序电路

重点是要看懂这张表
刷题:牛客-快速入门篇_第11张图片
刷题:牛客-快速入门篇_第12张图片

//VL21 根据状态转移表实现时序电路
`timescale 1ns/1ns

module seq_circuit(
      input                A   ,
      input                clk ,
      input                rst_n,
 
      output   wire        Y   
);

	reg Q0_n1;
	reg Q1_n1;
	
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n )begin
			Q0_n1 <= 1'd0;
			Q1_n1 <= 1'd0;
		end
		else begin
			Q0_n1 <= ~Q0_n1;
			Q1_n1 <= A ^ Q0_n1 ^ Q1_n1;
		end
	end
	
	assign Y = Q0_n1 & Q1_n1;
	


endmodule

VL22 根据状态转移图实现时序电路

刷题:牛客-快速入门篇_第13张图片

// VL22 根据状态转移图实现时序电路

`timescale 1ns/1ns

module seq_circuit(
   input                C   ,
   input                clk ,
   input                rst_n,
 
   output   reg        Y   
);

	parameter S0 = 2'b00,
			  S1 = 2'b01,
			  S2 = 2'b10,
			  S3 = 2'b11;
	
	reg [1:0] state;
	reg [1:0] next_state;
	
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n )begin
			state <= S0;
		end
		else 
			state <= next_state;
	end
	
	always@(*)begin
		case(state)
			S0: 
				if(C)
					next_state = S1;
				else
					next_state = S0;
			S1:
				if(~C)
					next_state = S3;
				else
					next_state = S1;
			S3:
				if(C)
					next_state = S2;
				else 	
					next_state = S3;
			S2:
				if(~C)
					next_state = S0;
				else 
					next_state = S2;
		default:next_state = S0;
		endcase 
	end
//综合出来后发现Y 延迟 一个周期,因此要用组合逻辑
/* 	always@(posedge clk or negedge rst_n)begin
		if(!rst_n )begin
			Y <= 1'd0;
		end
		else if(((state == S2) && C) || ((state == S3)))begin
			Y <= 1'd1;
		end
		else
			Y <= 1'd0;
	end */
	always@(*)begin
		case(state)
			S2: if(C)
					Y = 1'd1;
				else 	
					Y = 1'd0;
			S3: Y = 1'd1;
		default:Y = 1'd0;
		endcase
	end
	
endmodule

VL23 ROM的简单实现

推荐阅读:博客-介绍ROM声明、双端口ROM-FIFO


要求:实现一个深度为8,位宽为4bit的ROM,数据初始化为0,2,4,6,8,10,12,14。可以通过输入地址addr,输出相应的数据data

重点

  1. reg [3:0] SAVE [7:0] 表示:reg [数据位宽] SAVE [SAVE寄存器的个数]
    即:定义8个位宽为4的寄存器,这里的第几个就可以用输入的地址去索引ROM里的数据
  2. 需要先将ROM在rst_n有效时初始化上数据,然后一直保持里面的数据不变
//VL23 ROM的简单实现
`timescale 1ns/1ns
module rom(
	input clk,
	input rst_n,
	input [7:0]addr,
	
	output  [3:0]data
);

	reg  [3:0] SAVE [7:0];
	
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n )begin
			SAVE[7] = 8'd14;
			SAVE[6] = 8'd12;
			SAVE[5] = 8'd10;
			SAVE[4] = 8'd8;
			SAVE[3] = 8'd6;
			SAVE[2] = 8'd4;
			SAVE[1] = 8'd2;
			SAVE[0] = 8'd0;
		end
		else begin
			SAVE[7] = SAVE[7];
			SAVE[6] = SAVE[6];
			SAVE[5] = SAVE[5];
			SAVE[4] = SAVE[4];
			SAVE[3] = SAVE[3];
			SAVE[2] = SAVE[2];
			SAVE[1] = SAVE[1];
			SAVE[0] = SAVE[0];
		end
	end
	
	assign	data = SAVE[addr];

endmodule

VL24 边沿检测

要求:有一个缓慢变化的1bit信号a,编写一个程序检测a信号的上升沿给出指示信号rise,当a信号出现下降沿时给出指示信号down。注:rise,down应为单脉冲信号,在相应边沿出现时的下一个时钟为高,之后恢复到0,一直到再一次出现相应的边沿。
思路:

  1. 打2拍
  2. 单脉冲信号,需要使用中间变量
//VL24 边沿检测
`timescale 1ns/1ns
module edge_detect(
	input clk,
	input rst_n,
	input a,
	
	output reg rise,
	output reg down
);

	reg up_reg1,up_reg2;
	reg down_reg1,down_reg2;
	wire rise_reg;
	wire down_reg;
	
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n )begin
			up_reg1   <= 1'd0;
			up_reg2   <= 1'd0;
			down_reg1 <= 1'd0;
			down_reg2 <= 1'd0;
		end
		else begin
			up_reg1   <= a;
			up_reg2   <= up_reg1;
			down_reg1 <= a;
			down_reg2 <= down_reg1;
		end
	end
	
	assign rise_reg = (~up_reg2)   & up_reg1;
	assign down_reg = (~down_reg1) & down_reg2;
	
	always@(*)begin
		if(rise_reg)begin
			rise <= 1'd1;
			down <= 1'd0;	
		end
		else if(down_reg)begin
			rise <= 1'd0;
			down <= 1'd1;	
		end
		else begin
			rise <= 1'd0;
			down <= 1'd0;	
		end				
	end
		
endmodule

写在最后

至此牛客上入门篇的题目做完啦,开始进阶挑战!

你可能感兴趣的:(#,▶牛客刷题,fpga开发)