计算机组成与系统结构(第2版)(袁春风)中央处理器
及所学知识
把每条指令的执行分成多个大致相等的阶段,每个阶段在一个时钟周期内完成;各阶段内最多完成一次访存或一次寄存器读写或一次ALU操作;各阶段的执行结果在下一次时钟到来时保存到相应存储单元或稳定的保持在组合电路中;时钟周期的宽度以最复杂阶段所用时间为准,通常取一次存储器读写的时间。
- 取指令(IF):根据程序计数器PC中的指令地址,从存储器中取出一条指令。同时,PC根据指令子长度自动递增产生下一条指令所需要的指令地址,但遇到地址转移指令时,则控制器把转移地址送入PC,得到的地址做些变换送入PC。
- 指令译码(ID):对取指令操作中得到的指令进行分析和译码,确定这条指令需要完成的操作,从而产生相应的操作控制信号,用于驱动执行状态中的各种操作。
- 指令执行(EXE):跟据指令译码得到的操作控制信号,具体的执行指令动作,然后转移到结果写回状态。
- 存储器访问(MEM):所有需要访问存储器的操作都将在这个步骤中执行,该步骤给出存储器的数据地址,把数据写入到存储器中数据地址所指定的存储单元或者从存储器中得到数据地址单元中的数据。
- 结果写回(WB):指令执行的结果或者访问存储器中得到的数据写回相应的目的寄存器中。
MIPS指令可以分为三种类型,每种类型的指令需要不同的时钟周期数。
寄存器型(R型)指令
op | rs | rt | rd | shamt | funct |
---|---|---|---|---|---|
6位 | 5位 | 5位 | 5位 | 5位 | 6位 |
立即数型(I型)指令
op | rs | rt | immediate |
---|---|---|---|
6位 | 5位 | 5位 | 16位 |
跳转型(J型)指令
op | addr |
---|---|
6位 | 26位 |
op:操作码
rs:第1个源操作数寄存器
rt:第2个源操作数寄存器,或目的操作数寄存器
rd:目的操作数寄存器
shamt:位移量,移位指令用于指定移多少位
func:功能码,在R类型指令中用来指定指令的功能
immediate:16位立即数
addr:地址
MultiCycleCPU作为顶层模块,只有一个CLK(时钟信号)作为输入,时钟周期在仿真模块中设置。
MultiCycleCPU通过调用每个模块,并控制信号在相应模块间的传递来实现多周期CPU的功能。
PCctr计算下一条指令的地址,并对pc在刚开始进行置0初始化。
对于pc的改变有三种形式,beq指令,jump指令,其他。
要实现pc地址的正确计算,需要在取一条指令后,进行+4指向下一条指令,但是对于jump指令并不需要进行+4的操作。所以增加PCWre信号,当不为jump指令时设置位1。当PCWre为1时进行pc+4,对于jump指令则不进行这个操作。对于beq指令,则在译码产生Branch信号后在已经+4的pc基础上在加上扩展后的立即数即可。
PCctr模块设计如下,左侧是输入信号,右侧是输出信号。
端口名称 | 说明 |
---|---|
pc_in | 输入pc值 |
PCWre | pc写使能 |
imm | 取自指令后26位,对于beq则在此基础上截取后16位 |
Zero | 控制信号,用于beq指令 |
Branch | 控制信号,用于beq指令 |
Jump | 控制信号,用于jump指令 |
pc_out | 输出pc值 |
ControlUnit模块主要根据当前指令的op和func生成对应的控制信号,同时根据当前状态和操作码确定下一状态。
参照书上的11条MIPS指令,加上addu
指令,共兼容12条MIPS指令。
func | op | ALUctr | |
---|---|---|---|
addu | 000000 | 100001 | 000 |
addiu
、lw
、sw
、addu
的ALU控制信号取值一样,都是进行加法运算并不判溢出,记为addu操作。
subu
和beq
判0操作的ALU控制信号可看成一样,都做减法运算并不判溢出,记为subu操作。
ALU操作类型 | func | ALUctr |
---|---|---|
addu | 100001 | 000 |
add | 100000 | 001 |
or | 010 | |
subu | 100011 | 100 |
sub | 100010 | 101 |
sltu | 101011 | 110 |
slt | 101010 | 111 |
对于R-type型指令,除了ALUctr信号以外,其余控制信号的取值都相等
R—型指令的ALUctr信号的取值由其func字段决定
由上面func与ALUctr的对应关系,可得:
ALUctr[2]=!func[2] & func[1]
ALUctr[1]=func[3] & func[2] & func[1]
ALUctr[0]=!func[3] & !func[2] & !func[1] & !func[0] + !func[2] + func[1] + !func[0]
I—型指令,J—型指令的ALUctr控制信号的取值由op字段决定,可得编码方案:
ALUctr[2]=beq= !op[5] & !op[4] & !op[3] & op[2] & !op[1] & !op[0]
ALUctr[1]=ori= !op[5] & !op[4] & op[3] & op[2] & !op[1] & op[0]
ALUctr[0]=R-type= !op[5] & !op[4] & !op[3] & !op[2] & !op[1] & !op[0]
对于其他控制信号,采用编码方案有些复杂,采用判断的形式来生成它们的信号。
有限状态机控制器思想:
用一个有限状态机描述指令执行过程,由当前状态和操作码确定下一状态,每来一个时钟发生一次状态改变,不同状态输出不同的控制信号值,然后送到数据通路来控制指令的执行。
在设计时采用与书上的有限状态机控制器思想有些不同的思路,不采用不同状态输出不同控制信号。控制信号在ID(指令译码)阶段同时生成,增加输出信号IF_clk、ID_clk、ALU_clk、MEM_clk、WB_clk区分指令的不同阶段,根据不同的状态来修改这些信号的值。通过这些信号来触发指令的不同执行阶段。
在多周期CPU中,不同的指令分别占用不同的周期数。查阅资料,不同的指令参照如下的状态转移图:
最后,ControlUnit
模块设计如下(具体实现代码太多,只选取端口声明部分):
module ControlUnit(
clk,Opcode,func,
Branch,Jump,RegDst,ALUSrc,ALUctr,MemorReg,RegWr,MemWr,ExtOp,PCWre,
IF_clk,ID_clk,ALU_clk,MEM_clk,WB_clk,
state_out
);
input clk;
input [5:0] Opcode;
input [5:0] func;
output reg Branch;
output reg Jump;
output reg RegDst;
output reg ALUSrc;
output reg [2:0] ALUctr;
output reg MemorReg;
output reg RegWr;
output reg MemWr;
output reg ExtOp;
output reg PCWre;
output reg [2:0] state_out;
output reg IF_clk;
output reg ID_clk;
output reg ALU_clk;
output reg MEM_clk;
output reg WB_clk;
端口名称 | 说明 |
---|---|
clk | 时钟周期 |
Opcode | 操作码 |
func | 功能码,在R类型指令中用来指定指令的功能 |
Branch | beq |
Jump | jump |
RegDst | 选择写回结果的寄存器编号 |
ALUSrc | ALU输入来源 |
ALUctr | ALU操作码 |
MemorReg | 数据来自存储器还是ALU结果 |
RegWr | RegisterFile写使能 |
MemWr | 存储器写使能 |
ExtOp | 扩展方式 |
PCWre | 修改pc值 |
state_out | 状态输出 |
IF_clk | 取指信号 |
ID_clk | 指令译码信号 |
ALU_clk | 指令执行信号 |
MEM_clk | 存储器访问信号 |
WB_clk | 结果写回信号 |
ALU32.v
总体与第一次课程设计相同,根据此次多周期CPU进行适当修改。
其他模块的实现也比较简单,前面总体模块构成已大概介绍。比较难实现的是考虑各种信号在不同模块间的传递,以及将模块组合后可以协调工作顺利实现多周期CPU的功能。
这里只标识了主要信号的传递,别的信号大致采用同名表示同一信号传递。
除MultiCycleCPU外,主要模块大致都是左侧是输入信号,右侧是输出信号。
每1ps个时间单位时钟信号反转一次,即时钟周期为2ps
module MultiCycleCPU_test(
);
// module MultiCycleCPU(
// CLK,
// Opcode,ALU_in1,ALU_in2,ALU_result,STATE_out,PC_out,
// IF_clk,ID_clk,ALU_clk,MEM_clk,WB_clk
// );
reg CLK;
wire IF_clk,ID_clk,ALU_clk,MEM_clk,WB_clk;
wire [5:0] Opcode;
wire [31:0] ALU_in1;
wire [31:0] ALU_in2;
wire [31:0] ALU_result;
wire [31:0] PC_out;
MultiCycleCPU cpu32(
.CLK(CLK),
.Opcode(Opcode),
.ALU_in1(ALU_in1),
.ALU_in2(ALU_in2),
.ALU_result(ALU_result),
.PC_out(PC_out),
.IF_clk(IF_clk),
.ID_clk(ID_clk),
.ALU_clk(ALU_clk),
.MEM_clk(MEM_clk),
.WB_clk(WB_clk)
);
initial
begin
CLK=0;
end
always #0.001 CLK=~CLK;
endmodule
memory[0]=32'b000000_00111_00010_00010_00000_100000;//add reg[2]=reg[7]+reg[2]
memory[1]=32'b000000_00010_00111_00100_00000_100011;//subu reg[4]=reg[2]-reg[7]
memory[2]=32'b000000_00000_00001_00010_00000_101011;//sltu reg[0]
对照CLK、IF_clk、ID_clk、ALU_clk、MEM_clk、WB_clk所划分的周期可以很容易验证指令的执行情况。
module MultiCycleCPU(
CLK,
Opcode,ALU_in1,ALU_in2,ALU_result,PC_out,
IF_clk,ID_clk,ALU_clk,MEM_clk,WB_clk
);
input CLK;
output [5:0] Opcode;
output [31:0] ALU_in1;
output [31:0] ALU_in2;
output [31:0] ALU_result;
output [31:0] PC_out;//计算出的下一个pc值
output IF_clk,ID_clk,ALU_clk,MEM_clk,WB_clk;
//ControlUnit输出信号
wire PCWre,Branch,Jump,RegDst,ALUSrc,MemorReg,MemWr,ExtOp;
wire RegWr;
wire [2:0] ALUctr;
wire [2:0] STATE_out;
//ALU输出信号
wire Carryout,Zero,Overflow;
wire [31:0] PC_in;
//从指令中取出的相关数据
wire [25:0] Imm26;
wire [5:0] FUNC;
wire [4:0] RS;
wire [4:0] RT;
wire [4:0] RD;
wire [31:0] DataMem_out;
//RegisterFile相关
wire [31:0] data_to_registerfile;
//wire [31:0] ALU_data1;
wire [31:0] ALU_temp_data1;
wire [31:0] ALU_temp_data2;
//wire [31:0] ALU_data2;
// module PCctr(
// clk,PCWre,Branch, Jump, Zero, imm,pc_in,pc_out);
//imm取自指令后26位
PCctr pcctr(
.PCWre(PCWre),
.Branch(Branch),
.Jump(Jump),
.Zero(Zero),
.imm(Imm26),
.pc_in(PC_in),
.pc_out(PC_out)
);
// module PC_in_out(
// clk,pc_in,
// pc_out );
//把当前pc值PC_out,送入PC_in,以便PCctr下一次计算
PC_in_out pc_in_out(
.clk(CLK),
.pc_in(PC_out),
.pc_out(PC_in)
);
// module InstructionMem(
// pc,IF_clk,
// ins_Opcode,ins_func,ins_rs,ins_rt,ins_rd,ins_imm );
//imm取自指令后26位
InstructionMem IM(
.pc(PC_out),
.IF_clk(IF_clk),
.ins_Opcode(Opcode),
.ins_func(FUNC),
.ins_rs(RS),
.ins_rt(RT),
.ins_rd(RD),
.ins_imm(Imm26)
);
// module ControlUnit(
// clk,Opcode,func,
// Branch,Jump,RegDst,ALUSrc,ALUctr,MemorReg,RegWr,MemWr,ExtOp,PCWre,
// IF_clk,ID_clk,ALU_clk,MEM_clk,WB_clk,
// state_out
// );
ControlUnit controlunit(
.clk(CLK),
.Opcode(Opcode),
.func(FUNC),
.Branch(Branch),
.Jump(Jump),
.RegDst(RegDst),
.ALUSrc(ALUSrc),
.ALUctr(ALUctr),
.MemorReg(MemorReg),
.RegWr(RegWr),
.MemWr(MemWr),
.ExtOp(ExtOp),
.PCWre(PCWre),
.IF_clk(IF_clk),
.ID_clk(ID_clk),
.ALU_clk(ALU_clk),
.MEM_clk(MEM_clk),
.WB_clk(WB_clk),
.state_out(STATE_out)
);
// module RegisterFile(
// WB_clk,RegWr,Overflow,RegDst,Ra,Rb,Rc,busW,
// busA,busB );
RegisterFile registerfile(
.WB_clk(WB_clk),
.RegWr(RegWr),
.Overflow(Overflow),
.RegDst(RegDst),
.Ra(RS),
.Rb(RT),
.Rc(RD),
.busW(data_to_registerfile),
.busA(ALU_in1),
.busB(ALU_temp_data1)
);
// module Extender(
// ExtOp,ex_imm,
// ex_out
// );
Extender extender(
.ExtOp(ExtOp),
.ex_imm(Imm26),
.ex_out(ALU_temp_data2)
);
// module ALUSrc_select(
// ALUSrc,busB,imm36,
// src_out );
ALUSrc_select alusrc_select(
.ALUSrc(ALUSrc),
.busB(ALU_temp_data1),
.imm36(ALU_temp_data2),
.src_out(ALU_in2)
);
// module ALU32(
// ALU_clk,ALUctr,in0,in1,
// carryout,overflow,zero,out );
ALU32 alu32(
.ALU_clk(ALU_clk),
.ALUctr(ALUctr),
.in0(ALU_in1),
.in1(ALU_in2),
.carryout(Carryout),
.overflow(Overflow),
.zero(Zero),
.out(ALU_result)
);
// module DataMem(
// MEM_clk,WrEn,Adr,DataIn,
// DataOut
// );
DataMem datamem(
.MEM_clk(MEM_clk),
.WrEn(MemWr),
.Adr(ALU_result),
.DataIn(ALU_temp_data1),
.DataOut(DataMem_out)
);
// module Data_select(
// MemorReg,ALU_out,DataMem_out,
// Data_out
// );
Data_select data_select(
.MemorReg(MemorReg),
.ALU_out(ALU_result),
.DataMem_out(DataMem_out),
.Data_out(data_to_registerfile)
);
endmodule
module PCctr(
PCWre,Branch, Jump, Zero, imm,pc_in,pc_out
);
//input clk;
input PCWre;
input Branch;
input Jump;
input Zero;
input [25:0] imm;//imm取自指令的后26位
input [31:0] pc_in;
output reg [31:0] pc_out;
initial
begin
pc_out=0;
end
//clk上升沿触发
always @(Jump or Branch or Zero or PCWre)
if(Jump)
pc_out={pc_in[31:28],imm[25:0],1'b0,1'b0};
else if(Branch&&Zero)
pc_out=pc_in+{{14{imm[15]}},imm[15:0],1'b0,1'b0};
else if(!Jump&&PCWre)
pc_out=pc_in+4;
endmodule
module PC_in_out(
clk,pc_in,
pc_out
);
input clk;
input [31:0] pc_in;
output reg [31:0] pc_out;
always@(posedge clk)
begin
pc_out<=pc_in;
end
endmodule
module InstructionMem(
pc,IF_clk,
ins_Opcode,ins_func,ins_rs,ins_rt,ins_rd,ins_imm
);
input [31:0] pc;
input IF_clk;
output reg [5:0] ins_Opcode;
output reg [5:0] ins_func;
output reg [4:0] ins_rs;
output reg [4:0] ins_rt;
output reg [4:0] ins_rd;
output reg [25:0] ins_imm;
reg [31:0] memory[64:0];
initial
begin
memory[0]=32'b000000_00111_00010_00010_00000_100000;//add reg[2]=reg[7]+reg[2]
memory[1]=32'b000000_00010_00111_00100_00000_100011;//subu reg[4]=reg[2]-reg[7]
memory[2]=32'b000000_00000_00001_00010_00000_101011;//sltu reg[0]
module ControlUnit(
clk,Opcode,func,
Branch,Jump,RegDst,ALUSrc,ALUctr,MemorReg,RegWr,MemWr,ExtOp,PCWre,
IF_clk,ID_clk,ALU_clk,MEM_clk,WB_clk,
state_out
);
input clk;
input [5:0] Opcode;
input [5:0] func;
output reg Branch;
output reg Jump;
output reg RegDst;
output reg ALUSrc;
output reg [2:0] ALUctr;
output reg MemorReg;
output reg RegWr;
output reg MemWr;
output reg ExtOp;
output reg PCWre;
output reg [2:0] state_out;
output reg IF_clk;
output reg ID_clk;
output reg ALU_clk;
output reg MEM_clk;
output reg WB_clk;
parameter [2:0] IF=3'b000,//IF state
ID=3'b001,//ID state
EXE1=3'b110,//add,sub,subu,slt,sltu,ori,addiu
EXE2=3'b101,//beq
EXE3=3'b010,//sw lw
MEM=3'b011,//MEM state
WB1=3'b111,//add,sub,subu,slt,sltu,ori,addiu
WB2=3'b100;//lw
parameter [5:0] R_type=6'b000000,
ori=6'b001101,
addiu=6'b001001,
lw=6'b100011,
sw=6'b101011,
beq=6'b000100,
jump=6'b000010;
//状态寄存器
reg [2:0] state,next_state;
initial
begin
Branch=0;
Jump=0;
RegDst=0;
ALUSrc=0;
ALUctr=0;
MemorReg=0;
RegWr=0;
MemWr=0;
ExtOp=0;
PCWre=0;
state=3'b000;
state_out=state;
end
//上升沿触发
always@(posedge clk)
begin
state<=next_state;
state_out=state;
end
always@(state)
begin
case(state)
//IF阶段无条件跳转到ID阶段
IF: next_state<=ID;
//ID阶段
ID:
begin
if(Opcode == jump)
next_state<=IF;
else if(Opcode==beq)
next_state<=EXE2;
else if(Opcode==lw||Opcode==sw)
next_state<=EXE3;
else
next_state<=EXE1;
end
EXE1:next_state<=WB1;
EXE2:next_state<=IF;
EXE3:next_state<=MEM;
MEM:
begin
if(Opcode==lw)
next_state=WB2;
else
next_state=IF;
end
WB1:next_state<=IF;
WB2:next_state<=IF;
default:next_state=IF;
endcase
end
always @(next_state)
begin
if(next_state==IF)
IF_clk=1;
else
IF_clk=0;
if(next_state==ID)
ID_clk=1;
else
ID_clk=0;
if(next_state==EXE1||next_state==EXE2||next_state==EXE3)
ALU_clk=1;
else
ALU_clk=0;
if(next_state==MEM)
MEM_clk=1;
else
MEM_clk=0;
if(next_state==WB1||next_state==WB2)
WB_clk=1;
else
WB_clk=0;
end
always@(state)
begin
if(state==IF&&Opcode!=jump)
PCWre<=1;
else
PCWre<=0;
if(Opcode==R_type)
begin
Branch<=0;
Jump<=0;
RegDst<=1;
ALUSrc<=0;
MemorReg<=0;
RegWr<=1;
MemWr<=0;
ALUctr[2]<=(~func[2])&func[1];
ALUctr[1]<=func[3]&(~func[2])&func[1];
ALUctr[0]<=((~func[3])&(~func[2])&(~func[1])&(~func[0])) | ((~func[2])&func[1]&(~func[0]));
end
else
begin
if(Opcode==beq)
Branch<=1;
else
Branch<=0;
if(Opcode==jump)
Jump<=1;
else
Jump<=0;
RegDst<=0;
if(Opcode==ori||Opcode==addiu||Opcode==lw||Opcode==sw)
ALUSrc<=1;
else
ALUSrc<=0;
if(Opcode==lw)
MemorReg<=1;
else
MemorReg<=0;
if(Opcode==ori||Opcode==addiu||Opcode==lw)
RegWr<=1;
else
RegWr<=0;
if(Opcode==sw)
MemWr<=1;
else
MemWr<=0;
if(Opcode==addiu||Opcode==lw||Opcode==sw)
ExtOp<=1;
else
ExtOp<=0;
ALUctr[2]<=~Opcode[5]&~Opcode[4]&~Opcode[3]&Opcode[2]&~Opcode[1]&~Opcode[0];
ALUctr[1]<=~Opcode[5]&~Opcode[4]&Opcode[3]&Opcode[2]&~Opcode[1]&Opcode[0];
ALUctr[0]<=0;
end
end
endmodule
module RegisterFile(
WB_clk,RegWr,Overflow,RegDst,Ra,Rb,Rc,busW,
busA,busB
);
input WB_clk;
input RegWr;
input Overflow;
input RegDst;
//Ra(Rs),Rb(Rt),Rc(Rd)
input [4:0] Ra;
input [4:0] Rb;
input [4:0] Rc;
input [31:0] busW;
output reg [31:0] busA;
output reg [31:0] busB;
reg [31:0] RegMem [31:0];//32个32位宽寄存器
//初始化寄存器中的值
integer i;
initial
begin
// RegMem[0]<=5;
// RegMem[1]<=32'b11;
// RegMem[2]<=32'b10;
for(i=0;i<32;i=i+1)
RegMem[i]<=i;
end
always @(Ra or Rb)
begin
busA=RegMem[Ra];
busB=RegMem[Rb];
end
always@(posedge WB_clk or RegWr)
begin
//RegMem[2]<=2;
if(RegWr&&!Overflow)
begin
if(RegDst)
// RegMem[2]=2;
RegMem[Rc]<=busW;
else
RegMem[Rb]<=busW;
end
end
endmodule
module Extender(
ExtOp,ex_imm,
ex_out
);
input ExtOp;
input [25:0] ex_imm;
output [31:0] ex_out;
assign ex_out=ExtOp?{ {16{ex_imm[15]}} ,ex_imm[15:0]}:{ 16'b0,ex_imm[15:0]};
//符号扩展
// always @(ExtOp)
// begin
// if(ExtOp)
// ex_out={ {16{ex_imm[15]}} ,ex_imm[15:0]};
// else
// ex_out={ 16'b0,ex_imm[15:0]};
// end
endmodule
module ALUSrc_select(
ALUSrc,busB,imm36,
src_out
);
input ALUSrc;
input [31:0] busB;
input [31:0] imm36;
output [31:0] src_out;
assign src_out=ALUSrc?imm36:busB;
endmodule
module ALU32(
ALU_clk,ALUctr,in0,in1,
carryout,overflow,zero,out
);
input ALU_clk;
input [31:0] in0,in1;
input [2:0] ALUctr;//控制ALU进行何种操作
output reg[31:0] out;
output reg carryout,overflow,zero;
always@(posedge ALU_clk)
begin
case(ALUctr)
//addu
3'b000:
begin
{carryout,out}=in0+in1;
zero=(out==0)?1:0;
overflow=0;
end
//add
3'b001:
begin
out=in0+in1;
overflow=((in0[31]==in1[31])&&(~out[31]==in0[31]))?1:0;
zero=(out==0)?1:0;
carryout=0;
end
//or
3'b010:
begin
out=in0|in1;
zero=(out==0)?1:0;
carryout=0;
overflow=0;
end
//subu
3'b100:
begin
{carryout,out}=in0-in1;
zero=(out==0)?1:0;
overflow=0;
end
//sub
3'b101:
begin
out=in0-in1;
overflow=((in0[31]==0&&in1[31]==1&&out[31]==1)||(in0[31]==1&&in1[31]==0&&out[31]==0))?1:0;
zero=(in0==in1)?1:0;
carryout=0;
end
//sltu
3'b110:
begin
out=(in0>in1;
carryout=in0[in1-1];
overflow=0;
zero=(out==0)?1:0;
end
//sar
11'b00000000111:
begin
out=($signed(in0))>>>in1;
carryout=in0[in1-1];
overflow=0;
zero=(out==0)?1:0;
end
*/
endcase
end
endmodule
module DataMem(
MEM_clk,WrEn,Adr,DataIn,
DataOut
);
input MEM_clk;
input WrEn;
input [31:0] Adr;
input [31:0] DataIn;
output reg [31:0] DataOut;
//32个32位宽寄存器
reg [31:0] memory[0:31];
integer i;
initial
begin
for(i=0;i<32;i=i+1)
memory[i]<=0;
end
always@ (posedge MEM_clk)
begin
if(WrEn==0)
DataOut=memory[Adr];
else
memory[Adr]=DataIn;
end
endmodule
module Data_select(
MemorReg,ALU_out,DataMem_out,
Data_out
);
input MemorReg;
input [31:0] ALU_out;
input [31:0] DataMem_out;
output [31:0] Data_out;
assign Data_out=MemorReg?DataMem_out:ALU_out;
endmodule