Verilog/Verilog 실습
Verilog [CPU만들기] ACC
오버헤드프레스
2023. 5. 16. 17:29
ACC 코드
`timescale 1ns / 1ps
module 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
ACC 테스터 벤치 코드
`timescale 1ns / 1ps
module tb_acc();
reg clk, reset_p, acc_high_reset_p, acc_in_select, fill_value;
reg [1:0] acc_high_select, acc_low_select;
reg [3:0] aludata, bus_in;
wire [3:0] acc_high_data, acc_low_data;
acc DUT(
clk, reset_p, acc_high_reset_p,acc_in_select,fill_value,
acc_high_select, acc_low_select,
aludata, bus_in,
acc_high_data,
acc_low_data
);
initial begin
clk = 0; reset_p = 1; acc_high_reset_p = 1; acc_in_select = 0; fill_value = 0;
acc_high_select = 0; acc_low_select = 0;
aludata = 4'b0010;
bus_in = 4'b0101;
end
always #50 clk = ~clk;
initial begin
#100;
reset_p = 0; acc_high_reset_p = 0; #100;
acc_high_select = 2'b11; #100;
acc_high_select = 2'b00; #100;
acc_in_select = 1; acc_high_select = 2'b11; #100;
acc_high_select = 2'b00; acc_low_select = 2'b11; #100;
acc_low_select = 2'b00; #100;
acc_high_select = 2'b01; acc_low_select = 2'b01; #100;
acc_high_select = 2'b00; acc_low_select = 2'b00; #100;
acc_high_select = 2'b10; acc_low_select = 2'b10; #100;
acc_high_select = 2'b00; acc_low_select = 2'b00; #100;
acc_high_select = 2'b10; acc_low_select = 2'b01; #100;
acc_high_select = 2'b00; acc_low_select = 2'b00; #100;
acc_high_reset_p = 1; #100;
acc_high_reset_p = 0; #100;
$finish;
end
endmodule
곱셈 방법
곱셈 코드
`timescale 1ns / 1ps
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 input
);
assign acc_data = acc_o_en ? {acc_high_data, acc_low_data} : 8'bz;
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
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) 둘다 1이 나올 수 없기때문에 op_mul이 1이면 acc_low_data 를 보고
op_div가 1이면 cout을 보면 된다.
곱셈 테스터 벤치 코드
`timescale 1ns / 1ps
module tb_block_alu_acc( );
reg clk, reset_p, acc_high_reset_p,acc_in_select, acc_o_en;
reg op_add, op_sub, op_and, op_mul, op_div;
reg [1:0] acc_low_select, acc_high_select_in;
reg [3:0] bus_in, bus_reg_in;
wire sign_flag, zero_flag;
wire [7:0] acc_data;
block_alu_acc DUT(
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,
bus_in, bus_reg_in,
sign_flag, zero_flag,
acc_data
);
initial begin
clk = 0; reset_p = 1; acc_high_reset_p = 1; acc_in_select = 0; acc_o_en = 1;
op_add = 0; op_sub = 0; op_and = 0; op_mul = 0; op_div = 0;
acc_high_select_in = 0; acc_low_select = 0;
bus_reg_in = 4'b0101;
bus_in = 4'b0011;
end
always #50 clk = ~clk;
initial begin
#100;
reset_p = 0; acc_high_reset_p = 0; #100;
acc_in_select = 1; acc_high_select_in = 2'b11; #100;
acc_in_select = 0; acc_high_select_in = 2'b00; #100;
acc_low_select = 2'b11; #100;
acc_low_select = 2'b00; #100;
acc_high_reset_p = 1; #100;
acc_high_reset_p = 0; #100;
op_mul = 1; #100; //clk1
acc_high_select_in = 2'b01; acc_low_select = 2'b01; op_mul =0; #100;//clk2
acc_low_select = 2'b00; op_mul =1; #100; //clk3
acc_high_select_in = 2'b01; acc_low_select = 2'b01; op_mul =0; #100;//clk4
acc_low_select = 2'b00; op_mul =1; #100; //clk5
acc_high_select_in = 2'b01; acc_low_select = 2'b01; op_mul =0; #100; //clk6
acc_low_select = 2'b00; op_mul =1; #100; //clk7
acc_high_select_in = 2'b01; acc_low_select = 2'b01; op_mul =0; #100; //clk8
acc_high_select_in = 2'b00; acc_low_select = 2'b00; #100; //clk9
$finish;
end
endmodule
테스터 벤치 설명
op_mul = 1; #100; //clk1
acc_high_select_in = 2'b01; acc_low_select = 2'b01; op_mul =0; #100;//clk2
acc_low_select = 2'b00; op_mul =1; #100; //clk3
acc_high_select_in = 2'b01; acc_low_select = 2'b01; op_mul =0; #100;//clk4
acc_low_select = 2'b00; op_mul =1; #100; //clk5
acc_high_select_in = 2'b01; acc_low_select = 2'b01; op_mul =0; #100; //clk6
acc_low_select = 2'b00; op_mul =1; #100; //clk7
acc_high_select_in = 2'b01; acc_low_select = 2'b01; op_mul =0; #100; //clk8
acc_high_select_in = 2'b00; acc_low_select = 2'b00; #100; //clk9
$finish;
op_mul = 1일 때는 acc_high_select_in을 빼도 됨.
op_mul = 1이면 앞 코드
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];
이 부분에 의해 어차피 acc_high_select = 2'b11
곱셈 시뮬레이션
곱셈과 나눗셈은 코드가 동일하고 테스터 벤치만 바꿨다.
나눗셈 테스터 벤치 코드
`timescale 1ns / 1ps
module tb_block_alu_acc( );
reg clk, reset_p, acc_high_reset_p,acc_in_select, acc_o_en;
reg op_add, op_sub, op_and, op_mul, op_div;
reg [1:0] acc_low_select, acc_high_select_in;
reg [3:0] bus_in, bus_reg_in;
wire sign_flag, zero_flag;
wire [7:0] acc_data;
block_alu_acc DUT(
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,
bus_in, bus_reg_in,
sign_flag, zero_flag,
acc_data
);
initial begin
clk = 0; reset_p = 1; acc_high_reset_p = 1; acc_in_select = 0; acc_o_en = 1;
op_add = 0; op_sub = 0; op_and = 0; op_mul = 0; op_div = 0;
acc_high_select_in = 0; acc_low_select = 0;
bus_in = 4'b0111;
bus_reg_in = 4'b0010;
end
always #50 clk = ~clk;
initial begin
#100;
reset_p = 0; acc_high_reset_p = 0;
acc_in_select = 1; acc_high_select_in = 2'b11; #100; //clk1
acc_in_select = 0; acc_high_select_in = 2'b00;
acc_low_select = 2'b11; #100; //clk2
acc_low_select = 2'b00;
acc_high_reset_p = 1; #100; //clk3
acc_high_reset_p = 0;
acc_low_select = 2'b10; acc_high_select_in = 2'b10; #100;
acc_low_select = 2'b00; op_div = 1; #100;
acc_low_select = 2'b10; op_div = 0; acc_high_select_in = 2'b10; #100;
acc_low_select = 2'b00; op_div = 1; #100;
acc_low_select = 2'b10; op_div = 0; acc_high_select_in = 2'b10; #100;
acc_low_select = 2'b00; op_div = 1; #100;
acc_low_select = 2'b10; op_div = 0; acc_high_select_in = 2'b10; #100; // 맨 마지막에는 하위비트만 밀기
acc_low_select = 2'b00; op_div = 1; #100;
acc_low_select = 2'b10; op_div = 0; acc_high_select_in = 2'b00; #100;
acc_low_select = 2'b00; #100;
$finish;
end
endmodule
나눗셈 시뮬레이션