Verilog/Verilog 실습
Verilog [CPU만들기] CPU 제작
오버헤드프레스
2023. 5. 18. 15:20
processor_top 모듈
module processor_top(
input clk, btn,
input [3:0] col,
output [3:0] row,
output [3:0] com_an,
output [6:0] seg
);
wire [3:0] hout, lout, kout, data;
wire valid;
processor pro(
.clk(clk), .reset_p(btn),
.data(data),
.valid(valid),
.hout(hout), .lout(lout), .kout(kout)
);
key_scan ks(
.clk(clk),
.reset_n(~btn),
.col(col),
.row(row),
.key_value(data),
.valid(valid)
);
FND_4digit_switcher fnd( // 1ms에 1번씩 com_an 단자 바꿔줌
.value_1(kout),
.value_10(4'b1111),
.value_100(lout),
.value_1000(hout),
.clk(clk),
.com_an(com_an),
.seg(seg)
);
endmodule
processor
module processor(
input clk, reset_p,
input [3:0] data,
input valid,
output [3:0] hout, lout, kout
);
wire [7:0] mar_out;
wire [7:0] rom_out;
wire rom_en;
rom rom_256 (
.a(mar_out), // input wire [7 : 0] a // address 입력
.qspo_ce(rom_en), // input wire qspo_ce
.spo(rom_out) // output wire [7 : 0] spo
);
wire inreg_oen;
wire [7:0] int_bus;
register_N_bit_posedge #(.N(4)) inreg (
.D(data),
.clk(clk), .reset_p(reset_p), .wr_e_p(1'b1), .rd_e_p(inreg_oen),
.tri_state_Q(int_bus[7:4])
);
wire [3:0] keych_in;
assign keych_in = valid ? 4'b1111 : 4'b0000;
wire keych_oen;
register_N_bit_posedge #(.N(4)) keych_reg(.D(keych_in), .clk(clk), .reset_p(reset_p), .wr_e_p(1'b1), .rd_e_p(keych_oen), .tri_state_Q(int_bus[7:4]));
wire mar_inen;
register_N_bit_posedge #(.N(8)) mar (
.D(int_bus),
.clk(clk), .reset_p(reset_p), .wr_e_p(mar_inen),
.Q(mar_out)
);
wire mdr_inen, mdr_oen;
register_N_bit_posedge #(.N(8)) mdr (
.D(rom_out),
.clk(clk), .reset_p(reset_p), .wr_e_p(mdr_inen), .rd_e_p(mdr_oen),
.tri_state_Q(int_bus)
);
wire [7:0] ir_out;
wire ir_inen;
register_N_bit_posedge #(.N(8)) ir (
.D(int_bus),
.clk(clk), .reset_p(reset_p), .wr_e_p(ir_inen),
.Q(ir_out)
);
wire pc_inc, load_pc, pc_oen;
program_addr_counter pc (
.clk(clk), .reset_p(reset_p), .pc_inc(pc_inc), .load_pc(load_pc), .pc_o_en(pc_oen),
.pc_in(int_bus),
.pc_out(int_bus)
);
wire breg_inen;
wire [3:0] bus_reg_in;
register_N_bit_posedge #(.N(4)) breg (
.D(int_bus[7:4]),
.clk(clk), .reset_p(reset_p), .wr_e_p(breg_inen),
.Q(bus_reg_in)
);
wire tmpreg_inen, tmpreg_oen;
register_N_bit_posedge #(.N(4)) tmpreg (
.D(int_bus[7:4]),
.clk(clk), .reset_p(reset_p), .wr_e_p(tmpreg_inen), .rd_e_p(tmpreg_oen),
.tri_state_Q(int_bus[7:4])
);
wire creg_inen, creg_oen;
register_N_bit_posedge #(.N(4)) creg (
.D(int_bus[7:4]),
.clk(clk), .reset_p(reset_p), .wr_e_p(creg_inen), .rd_e_p(creg_oen),
.tri_state_Q(int_bus[7:4])
);
wire dreg_inen, dreg_oen;
register_N_bit_posedge #(.N(4)) dreg (
.D(int_bus[7:4]),
.clk(clk), .reset_p(reset_p), .wr_e_p(dreg_inen), .rd_e_p(dreg_oen),
.tri_state_Q(int_bus[7:4])
);
wire rreg_inen, rreg_oen;
register_N_bit_posedge #(.N(4)) rreg (
.D(int_bus[7:4]),
.clk(clk), .reset_p(reset_p), .wr_e_p(rreg_inen), .rd_e_p(rreg_oen),
.tri_state_Q(int_bus[7:4])
);
wire acc_high_reset_p,acc_in_select, acc_o_en;
wire op_add, op_sub, op_and, op_mul, op_div;
wire[1:0] acc_low_select, acc_high_select_in;
wire sign_flag, zero_flag;
block_alu_acc alu_acc(
clk, reset_p, acc_high_reset_p,acc_in_select, acc_o_en,
op_add, op_sub, op_and, op_mul, op_div,
acc_low_select, acc_high_select_in,
int_bus[7:4], bus_reg_in,
sign_flag, zero_flag,
int_bus //3상 출력
);
wire outreg_inen;
wire [7:0] outreg_out;
register_N_bit_posedge #(.N(8)) outreg (
.D(int_bus),
.clk(clk), .reset_p(reset_p), .wr_e_p(outreg_inen), .rd_e_p(1'b1),
.Q(outreg_out)
);
wire [15:0] bcd;
assign hout = bcd[7:4];
assign lout = bcd[3:0];
bin_to_dec b2d(
.bin({4'b0000, outreg_out}),
.bcd(bcd)
);
wire keyout_inen;
register_N_bit_posedge #(.N(4)) keyout (
.D(int_bus[7:4]),
.clk(clk), .reset_p(reset_p), .wr_e_p(keyout_inen), .rd_e_p(1'b1),
.tri_state_Q(kout)
);
control_block_top control_block( //control block 이 topmodule
.clk(clk), .reset_p(reset_p),
.ir_in(ir_out), //ir레지스터의 출력 받아오면 됨
.zero_flag(zero_flag), .sign_flag(sign_flag),
.mar_inen(mar_inen), .mdr_inen(mdr_inen), .mdr_oen(mdr_oen), .ir_inen(ir_inen),
.pc_inc(pc_inc), .load_pc(load_pc), .pc_oen(pc_oen),
.breg_inen(breg_inen), .tmpreg_inen(tmpreg_inen), .tmpreg_oen(tmpreg_oen),
.creg_inen(creg_inen), .creg_oen(creg_oen),
.dreg_inen(dreg_inen), .dreg_oen(dreg_oen), .rreg_inen(rreg_inen), .rreg_oen(rreg_oen),
.acc_high_reset_p(acc_high_reset_p),
.acc_in_select(acc_in_select), .acc_o_en(acc_o_en), .op_add(op_add), .op_sub(op_sub),
.op_and(op_and), .op_mul(op_mul), .op_div(op_div),
.outreg_inen(outreg_inen), .inreg_oen(inreg_oen), .keych_oen(keych_oen), .keyout_inen(keyout_inen),
.rom_en(rom_en),
.acc_low_select(acc_low_select), .acc_high_select_in(acc_high_select_in)
);
endmodule
각 레지스터는 register_N_bit_posedge 모듈 사용
register_N_bit_posedge 모듈
module register_N_bit_posedge #(parameter N = 8)(//함수 선언, 회로로 만들어주지않음 initial문, parameter문 등
input [N-1:0] D,
input clk,
input reset_p,
input wr_e_p, rd_e_p, //1일때는 shift, 0일 때는 load
output [N-1:0] Q,
output [N-1:0] tri_state_Q
);
wire [N-1:0] and_o;
wire [N-1:0] F;
genvar i; //회로 만들때는 무시되고 generate안에서만 사용됨, 아래 generate문 안에 있는 것들은 회로로 만들어줌
generate
for (i=0; i<N; i=i+1)begin
mux_2_1 mx(.D({D[i], Q[i]}), .S(wr_e_p), .F(F[i]));
and (and_o[i], F[i], ~reset_p);
D_flip_flop_posedge DFF (.D(and_o[i]), .E(clk), .Q(Q[i]));
bufif1 (tri_state_Q[i], Q[i],rd_e_p); //rd_e_n이 0일 때 Q로 나간다. rd_e_p일 때 0이면 Q는 임피던스
end
endgenerate //아래것들을 위처럼 간단하게 가능
endmodule
module register (
input d, clk, reset_p, in_en,
output q
);
reg q_temp;
always @(posedge clk or posedge reset_p)begin
if(reset_p) q_temp = 0;
else if(in_en) q_temp = d;
else q_temp = q_temp;
end
assign q = q_temp;
endmodule
block_alu_acc
module block_alu_acc(
input clk, reset_p, acc_high_reset_p,acc_in_select, acc_o_en,
input op_add, op_sub, op_and, op_mul, op_div,
input [1:0] acc_low_select, acc_high_select_in,
input [3:0] bus_in, bus_reg_in,
output sign_flag, zero_flag,
output [7:0] acc_data
);
wire carry_flag, cout;
wire [1:0] acc_high_select;
wire [3:0] aludata, acc_high_data, acc_low_data;
assign acc_high_select[1] = (op_mul|op_div) ? (op_mul&acc_low_data[0]) | (op_div&cout) : acc_high_select_in[1];
assign acc_high_select[0] = (op_mul|op_div) ? (op_mul&acc_low_data[0]) | (op_div&cout) : acc_high_select_in[0];
//(op_mul|op_div) 곱셈을 할거라 | 사용. (op_mul&acc_low_data[0]),(op_mul&acc_low_data[0]) = 00 또는 11밖에 안나옴
//op_mul일 때도 더하는데 acc_low_data가 0이면 안나오고 1이면 나올것.
//뺀결과가 0이면 양수, 뺀 결과가 1이면 음수(뺄 수 있으면 cout = 1), (op_div&cout)도 00 또는 11만 나옴
acc block_acc(
clk, reset_p, acc_high_reset_p,acc_in_select, carry_flag, //1bit input
acc_high_select, acc_low_select, //2bit input
aludata, bus_in, //4bit input
acc_high_data, acc_low_data //4bit output
);
assign acc_data = acc_o_en ? {acc_high_data, acc_low_data} : 8'bz; //tri state buf
alu block_alu(
clk, reset_p,
op_add, op_sub, op_and, op_mul, op_div, acc_low_data[0],
acc_high_data, //<<acc_high
bus_reg_in, //<< bus_reg_data
aludata, //<<alu_out
zero_flag, sign_flag, carry_flag, cout
);
endmodule
acc와 half acc 코드
odule acc(
input clk, reset_p, acc_high_reset_p,acc_in_select,fill_value,
input [1:0] acc_high_select, acc_low_select,
input [3:0] aludata, bus_in,
output [3:0] acc_high_data,
output [3:0] acc_low_data
);
wire [3:0] acc_high_in;
assign acc_high_in = acc_in_select ? bus_in : aludata;
half_acc acc_high(
//high만 reset해줄 필요가 있음. 상위4bit를 넘기고 reset해줘야함. 하위 4bit를 넘기는애는 어차피 넘겨서 안해도 됨
.clk(clk), .reset_p(reset_p | acc_high_reset_p), .load_msb(fill_value), .load_lsb(acc_low_data[3]), //load_msb 최상위 비트에 들어가는것
.S(acc_high_select), //S입력이 0 0일 때는 아무것도 안함. 0 1일 때는 shift right, 10일 때는 shift left, 11일 때는 load(외부에서 4bit 받을 거)
.data_in(acc_high_in), //입력을 bus로 받을건지, alu로 받을건지 결정 가능해짐
.data(acc_high_data));
half_acc acc_low(
//high만 reset해줄 필요가 있음. 상위4bit를 넘기고 reset해줘야함. 하위 4bit를 넘기는애는 어차피 넘겨서 안해도 됨
.clk(clk), .reset_p(reset_p), .load_msb(acc_high_data[0]), .load_lsb(fill_value), //load_msb 최상위 비트에 들어가는것
.S(acc_low_select), //S입력이 0 0일 때는 아무것도 안함. 0 1일 때는 shift right, 10일 때는 shift left, 11일 때는 load(외부에서 4bit 받을 거)
.data_in(acc_high_data),
.data(acc_low_data));
endmodule
module half_acc (
input clk, reset_p, load_msb, load_lsb,
//load_msb 최상위 비트에 들어가는것, load_msb는 하위 4bit로 미는데, 하위 4bit는 shift_right하고 상위 4bit를 따로 채우면 됨
input [1:0] S,
//S입력이 0 0일 때는 아무것도 안함. 0 1일 때는 shift right, 10일 때는 shift left, 11일 때는 load(외부에서 4bit 받을 거)
input [3:0] data_in,
output [3:0] data
);
wire [3:0] D;
mux_4_1 mux3(.D({data_in[3], data[2], load_msb, data[3]}), .S(S), .F(D[3])); //순서: load > left > right > pass
mux_4_1 mux2(.D({data_in[2], data[1], data[3], data[2]}), .S(S), .F(D[2])); //D(2번bit, 1번 bit, 0번 bit, 번 bit)
mux_4_1 mux1(.D({data_in[1], data[0], data[2], data[1]}), .S(S), .F(D[1])); //최상위 비트를 load_msb입력 받을 때는 다른bit로 채움
mux_4_1 mux0(.D({data_in[0], load_lsb, data[1], data[0]}), .S(S), .F(D[0]));
register_N_bit_posedge #(.N(4))reg_acc_4bit(.D(D), .clk(clk), .reset_p(reset_p), .wr_e_p(1'b1), .rd_e_p(1'b1), .Q(data));
//tri_state_Q 는 rd_e_p가 0일 때 임피던스 나옴, 1이면 1출력
endmodule
alu 코드
module alu(
input clk, reset_p, op_add, op_sub, op_and, op_mul, op_div, alu_lsb,
input [3:0] acc_high,
input [3:0] bus_reg_data,
output [3:0] alu_out,
output zero_flag, sign_flag, carry_flag, cout
);
wire [3:0] sum;
wire zero_sum;
fadd_subNbit #(.N(4))fadd_sub4bit(.A(acc_high), .B(bus_reg_data), .Cin(op_sub|op_div), .S(sum), .Cout(cout));
assign alu_out = op_and ? (acc_high & bus_reg_data) : sum;
//op_and가 1일 때라는 뜻,
//acc_high & bus_reg_data이게 나옴. 외에는 sum
//2x1 mux, 조건연산자는 mux가 만들어진다.
register sign_f(.d(!(cout)&op_sub), .clk(clk), .reset_p(reset_p), .in_en(op_sub), .q(sign_flag));
//결과가 -일 때 carry는 0. 음수가 발생하면 flag
register carry_f(.d((op_add|op_div|(op_mul&alu_lsb))&cout), .clk(clk), .reset_p(reset_p), .in_en(1'b1), .q(carry_flag));
//alu_lsb는 곱셈할 때
assign zero_sum = ~(|sum); //zero_sum은 sum이 0일 때 1. sum = 1일 때 or연산이 0이어야 하기때문에 식을 저렇게 함
register zero_f(.d(zero_sum), .clk(clk), .reset_p(reset_p), .in_en(op_sub), .q(zero_flag));
//연산의 결과가 0일때 zero flag 발생
endmodule
processor_top 테스터벤치
module tb_processor_top();
reg clk, reset_p;
reg [3:0] data;
reg valid;
wire [3:0] hout, lout, kout;
integer i, j, k;
processor pr(.clk(clk), .reset_p(reset_p), .data(data), .valid(valid), .hout(hout), .lout(lout), .kout(kout));
initial begin
clk = 0; reset_p = 1; data = 0; valid = 0;
i = 0; k = 10; j = 0;
end
always #50 clk = ~clk;
initial begin
#100;
reset_p = 0;
#100;
for (k = 10; k < 15; k = k + 1)begin
for (i = 0; i < 10; i = i + 1)begin
for (j = 0; j < 10; j = j + 1)begin
data = i; valid = 1; #300_000 //valid = 1은 키보드 입력이 있다는 뜻 first number
valid = 0; #300_000;
data = k; valid = 1; #300_000;
valid = 0; #300_000;
data = j; valid = 1; #300_000;
valid = 0; #300_000;
data = 4'b1111; valid = 1; #300_000; //equal을 주려고 F를 함.
valid = 0; #300_000;
$display("%d %d %d = %d %d : %d \n", i, k, j, hout, lout, kout); //print랑 똑같음, 콘솔창에 문자열을 출력해줌
end
end
end
$finish;
end
D-FlipFlop 을 사용했는데 S-R래치 포함이 아닌 동작적 모델링을 통해 제작해야한다.
FPGA에서는 S-R래치를 만들 수 없음. 회로가 작을 때는 만들어 진다.
D-FlipFlop 동작적 모델링 코드
module D_flip_flop_posedge(
input D,
input E,
output reg Q
);
always @(posedge E)begin
if(E) Q = D;
else Q = Q;
end
// wire Ebar, QX;
// not (Ebar, E);
// D_flip_flop DFF1(.D(D), .E(Ebar), .Q(QX));
// D_flip_flop DFF2(.D(QX), .E(E), .Q(Q), .Qbar(Qbar));
endmodule
각 식의 결과
15가 나오는건 아무 값도 안들어가서 1111 15출력
헥사값으로 변환후 진행하면 and가 정상적으로 실행
11은 음수를 표현하는 것이다.
시뮬레이션 결과
spo는 ROM에서 나오는 값
Q는 mar의 출력
tri_state_Q는 mdr 출력
덧셈
뺄셈
곱셈
나눗셈
Basys3 flash memory에 자동으로 저장되어 전원을 껐다 켜도 실행되게 하기
정상적으로 작동하는 것을 확인했다.
cpu전반 알고리즘에 대해 학습할 수 있었다.