2008年2月19日火曜日

TD4.v CPUの創り方

module top;

reg ck, reset;

initial begin

$dumpfile("cpu.vcd");
$dumpvars(0,CPU);

#0 ck=0;
#0 reset=1;

#100 ck=~ck;
#100 ck=~ck;

#0 reset=0;

#100 ck=~ck;
#100 ck=~ck;

#100 ck=~ck;
#100 ck=~ck;

#100 ck=~ck;
#100 ck=~ck;

#100 ck=~ck;
#100 ck=~ck;

#100 ck=~ck;
#100 ck=~ck;

#100 ck=~ck;
#100 ck=~ck;

#100 ck=~ck;
#100 ck=~ck;

#100 ck=~ck;
#100 ck=~ck;

$finish;

end

cpu CPU (.ck(ck), .reset(reset));

initial $monitor ("ck:rs = %b:%b", ck, reset);
endmodule

module rom(rom_address, rom_data);

/*
instruction decode
0000_0000 : add r0,r1
0001_0000 : add r1,r0
0010_.... : add r0,#i
0011_.... : add r1,#i
0100_0000 : move r0,r1
0101_0000 : move r1,r0
0110_.... : move r0,#i
0111_.... : move r1,#i
1000_.... : jmp #i
1100_.... : jnz #i

ld(on)
0001 : r0
0010 : r1
0100 : r2
1000 : r3(PC)
*/
input [3:0] rom_address;
output [7:0] rom_data;

wire [3:0] rom_address;
reg [7:0] rom_data;

always @ (rom_address) begin
casez(rom_address)
4'h0: rom_data=8'b0110_0001; /* move r0,#0001 */
4'h1: rom_data=8'b0010_0010; /* add r0,#0010 */
4'h2: rom_data=8'b0111_0000; /* move r1,#0000 */
4'h3: rom_data=8'b1100_0001; /* jnz #0001 */
4'h4: rom_data=8'b1000_0001; /* jmp #0001 */
default:
rom_data=8'bxxxx_xxxx;
endcase
/*$display("%h:%h",rom_address,rom_data);*/
end

endmodule

module dec (
i_code,
co,
ld,
on,
oz,
oi
);
input [3:0] i_code;
input co;
output [3:0] ld;
output [3:0] on;
output oz;
output oi;

reg [3:0] ld, on;
reg oz, oi;
always @ (i_code) begin
casez (i_code)
4'b0000: begin /* add r0,r1 */ ld=4'b0001; on=4'b0010; oz=0; oi=0; end
4'b0001: begin /* add r1,r0 */ ld=4'b0010; on=4'b0001; oz=0; oi=0; end
4'b0010: begin /* add r0,#i */ ld=4'b0001; on=4'b0001; oz=0; oi=1; end
4'b0011: begin /* add r1,#i */ ld=4'b0010; on=4'b0010; oz=0; oi=1; end
4'b0100: begin /* move r0,r1 */ ld=4'b0001; on=4'b0010; oz=0; oi=0; end
4'b0101: begin /* move r1,r0 */ ld=4'b0010; on=4'b0001; oz=0; oi=0; end
4'b0110: begin /* move r0,#i */ ld=4'b0001; on=4'b0001; oz=0; oi=1; end
4'b0111: begin /* move r1,#i */ ld=4'b0010; on=4'b0010; oz=0; oi=1; end
4'b1000: begin /* jmp #i */ ld=4'b1000; on=4'bxxxx; oz=1; oi=1; end
4'b1100: begin /* jnz #i */
if (co) begin
ld=4'b1000; on=4'bxxxx; oz=1; oi=1;
end else begin
ld=4'b0001; on=4'b0001; oz=1; oi=0; /* add r0,#0 as NOP */
end
end
default: begin
ld=4'bxxxx;
on=4'bxxxx;
oz=1'bx;
oi=1'bx;
end
endcase
end
endmodule

module registor (
ck,reset,r_bus,
r0_ld, r1_ld, r2_ld, r3_ld,
r0_on, r1_on, r2_on, r3_on,
co,
ra,
rb,
rc,
ia
);

input ck, reset;
input r0_ld, r1_ld, r2_ld, r3_ld;
input r0_on, r1_on, r2_on, r3_on;
input [3:0] r_bus;
input co;
output [3:0] ra, rb, ia;
output rc;
reg [3:0] r0, r1, r2,r3;
reg carry;

always @ (posedge ck) begin
#1;
if (reset) begin
r0=4'h0;
r1=4'h0;
r2=4'h0;
r3=4'h0;
carry=0;
end else begin
if (r0_ld) r0=r_bus;
if (r1_ld) r1=r_bus;
if (r2_ld) r2=r_bus;
if (r3_ld) r3=r_bus; else r3=r3+1;
carry=co;
end
$display("r0 = %h r1 = %h r2 = %h r3 = %h", r0,r1,r2,r3);
end

assign ra =
(r0_on==1) ? r0 :
(r1_on==1) ? r1 :
(r2_on==1) ? r2 :
(r3_on==1) ? r3 : 4'bxxxx ;

assign rb =
(r0_ld==1) ? r0 :
(r1_ld==1) ? r1 :
(r2_ld==1) ? r2 :
(r3_ld==1) ? r3 : 4'bxxxx ;

assign rc = carry;

assign ia = r3;

endmodule

module alu (a, b, im, oz, oi, q, of, co);
input [3:0] a, b, im;
input oz, oi;
output [3:0] q;
output of, co;
assign {of,q} =
(oi == 1 && oz == 1)? im :
(oi == 1)? a + im :
a + b ;
assign co=| {of,q} ;
endmodule

module cpu(ck, reset) ;
input ck, reset;

wire [3:0] rom_address;
wire [7:0] rom_data;
wire [3:0] ld, on;
wire oz, oi;

wire [3:0] r_bus;
wire [3:0] ra, rb;
wire rc;
wire co;

rom rom (.rom_address(rom_address), .rom_data(rom_data));

dec DEC (.i_code(rom_data[7:4]), .co(rc), .ld(ld), .on(on), .oz(oz), .oi(oi));

registor REG_ ( .ck(ck), .reset(reset), .r_bus(r_bus), .r0_ld(ld[0]), .r1_ld(ld[1]), .r2_ld(ld[2]), .r3_ld(ld[3]), .r0_on(on[0]), .r1_on(on[1]), .r2_on(on[2]), .r3_on(on[3]), .co(co), .ra(ra), .rb(rb), .rc(rc), .ia(rom_address));

alu ALU (.a(ra), .b(rb), .im(rom_data[3:0]), .oz(oz), .oi(oi), .q(r_bus), .co(co));

endmodule

0 件のコメント: