verilog 多周期控制器

用Verilog写一个控制码类型的状态机,用来控制一个A+B+C+/-D类型的多周期数据通路,这个运算被分为R←A,R←R+B,R←R+C.R←R+ D.或R←R- D四个周期,启动运算后,只需要一定的硬件资源,在几个时钟周期内对有限的硬件进行重用从而完成需要的功能。控制码类型的状态机和FSM类型的状态机不一样,控制码类型的状态机需要提前列好控制码表,当然最为简单常用的其实是FSM类型的状态机

核心代码:
用于与其他模块交互以及和外部信号连接的Virtual_Lab_Top.v文件中的代码:

//状态机实验
`default_nettype none 
module Virtual_Lab_Top 			//信号的输入和输出
(
    input  wire  CLOCK,
    input  wire  [4:0] BUTTON, //按键
    input  wire [35:0] SWITCH, //开关
    output wire [35:0] LED,    //指示灯 
    output wire  [3:0] HEX0   //数码管
); 

reg start,done_moore;
wire done;
wire [9:0]control;

//输入端口赋值给内部信号
wire [3:0] A = SWITCH[3:0];
wire [3:0] B = SWITCH[7:4];
wire [3:0] C = SWITCH[11:8];
wire [3:0] D = SWITCH[15:12];
wire mode = SWITCH[16];
wire start_asyn = BUTTON[0];
wire _reset =BUTTON[1];

//内部信号赋值给输出端口(数码管)观察
wire [3:0] result;
assign HEX0 = result;
assign LED[0] = done_moore;

//启动信号
always@(posedge CLOCK or posedge _reset or posedge done)
begin 
	if(_reset==1 || done==1)
		start<=1'b0; 
	else 
		start<= start_asyn; 
end

//计算完成信号
always@(posedge CLOCK or posedge _reset or posedge start_asyn)
begin
	if(_reset==1 || start_asyn==1)
		done_moore<=1'b0; 
	else 
		done_moore<= done; 
end

 controller u10(CLOCK,_reset,start,mode,control,done); 
 data_path  u11(CLOCK,mode,A,B,C,D,control,result);
 
endmodule

用于初始化控制码存储器以及控制状态机运行的控制器模块controller.v文件:

module controller(
	input CLOCK,_reset,start,
	input mode,
	output reg[4:0] control, 
	output reg done
);

reg [2:0]mpc;//control memory
(*ramgtyle="M512"*) reg[10:0]cm[0:5]; 	//control memory,using
													//a built-in memory
//reg [0:5][10:0] cm; 
reg [1:0] ccode;//branch type 
reg [2:0] jump_address;//branch address 
reg load;

//--Initialize the CM,---
initial begin 
cm[0]=11'h400;//wait for start=1
cm[1]=11'h020;//initialize 
cm[2]=11'h060;
cm[3]=11'h0E0;
cm[4]=11'h768;//if mode=0,done=1,goto 0
cm[5]=11'h3F8;//done=1,goto 0
end

//------------MUX----------
always@(*)
begin 
	case(ccode)
	2'b00:load<=1'b0;//next instruction
	2'b01:load<=1'b1;//unconditional jump
	2'b10:if (start==0) //conditional jump if start=0
			load<=1'b1; 
	else
			load<=1'b0;
	2'b11:if(mode==0) //conditional jump if mode=0
			load<=1'b1; 
	else
			load<=1'b0;
	endcase 
end

//---------MPC-----------
always@(posedge CLOCK or posedge _reset)
begin 
	if(_reset == 1)
		mpc <=3'b000;
	else 
		if(load==0)
			mpc <= mpc+1;
		else 
			mpc <= jump_address;
end

//--------CM----------------
always@(*) 
begin 
	ccode=cm[mpc][10:9];
	control=cm[mpc][8:4];
	done=cm[mpc][3]; 
	jump_address=cm[mpc][2:0]; 
end 

endmodule 

用于控制在特定状态下进行特定数据处理的数据处理模块data_path.v文件:

module data_path(
	input CLOCK,mode, 
	input [3:0] A,B,C,D, 
	input [4:0]control, 
	output reg [3:0]result
);

reg [3:0] MUX1,MUX2,MUX;

wire m=control[0]; 
wire e=control[1]; 
wire  s0=control[2], 
		s1=control[3],
		s2=control[4]; 

always@(posedge CLOCK)//registers and cntr 
begin 
	case({s2,s1})
		2'b00:MUX1<=B;
		2'b01:MUX1<=C;
		2'b10:MUX1<=D;
		2'b11:MUX1<=D; 
		default:MUX1 <= MUX1; 
		endcase 
	case(s0)
		1'b0:MUX2<=A;
		1'b1:MUX2<=4'h0;
		default:MUX2<=MUX2; 
	endcase 
end

always@(posedge CLOCK)
begin 
	if(e)
	begin
	if(m==1'b0)
	begin
		if(s0==1'b0)
			result<=MUX2;
		else if({s2,s1}==2'b10 && mode==1)
			result<=result;
		else
			result<=result+MUX1;
	end
	else
		result<=result-MUX1;
	end
end 

endmodule 

另外也要接入内部时钟,跟我之前流水灯的文章中一样,采用偶分频方法将原有的时钟频率分频,参数RATIO调到100左右,不行再调大或调小时钟频率试试

WeLab虚拟面板设置图片:
在这里插入图片描述
start为启动键,mode置1为A+B+C-D,置0为A+B+C+D,A、B、C、D输入四个四位二进制运算数据,reset为重置按键,数码管显示的是运算结果。输入运算数据后确定运算模式然后按下启动键就能得到运算结果

注意:该工程写的是时序电路,时序电路非常关键的是最小时钟周期即最大时钟频率的确定,时钟周期太短会使得硬件无法在时钟周期内无法完成预定的工作就进入下一个周期,太长会使得整个运算还未完成软件就返回了不正确的显示数据,因为时钟周期环环紧扣,从而引发一系列的时序逻辑错误。若确定代码部分没有错误,但是却无法运行出正确的功能,那就应该是选定的时钟周期不合适。博主尚未掌握准确的电路延时计算方法,所以只能耐心地调试时钟分频系数来调大或调小时钟周期,试出一个合适的时钟周期

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值