☑️ 8bit adder / fnd 출력
- Block diagram
4bit adder, fnd 출력 시스템에서 adder만 8bit adder로 바꾸어주면 된다.
8bit adder의 경우 4bit adder 두개로 구성된다.
- adder.v
`timescale 1ns / 10ps
// top module - calculator
module calculator(
input wire i_cal_clk,
input wire i_cal_reset,
input wire [7:0] i_cal_a,
input wire [7:0] i_cal_b,
output [3:0] cal_fndCom_o,
output [7:0] cal_fndFont_o
);
wire [7:0] w_sum;
wire w_carry;
// assign ca_fndCom_o = 4'b1110;
fullAdder_8bit U_8bit_adder(
.i_fa8_a(i_cal_a),
.i_fa8_b(i_cal_b),
.i_fa8_cin(1'b0), // initial value
.fa8_sum_o(w_sum),
.fa8_carry_o(w_carry)
);
fnd_controller U_FND_con(
.i_con_clk(i_cal_clk),
.i_con_reset(i_cal_reset),
.i_con_bcdData({5'b0, w_carry, w_sum}), // 14bit format에 맞추어 남는 나머지 bit를 gnd로 연결
.con_fndCom_o(cal_fndCom_o),
.con_segData_o(cal_fndFont_o)
);
endmodule
module fullAdder_8bit(
input [7:0] i_fa8_a,
input [7:0] i_fa8_b,
input i_fa8_cin,
output [7:0] fa8_sum_o,
output fa8_carry_o
);
wire w_carry;
Adder_4bit U_A4_1(
.i_fa4_a(i_fa8_a[3:0]),
.i_fa4_b(i_fa8_b[3:0]),
.i_fa4_cin(i_fa8_cin),
.fa4_sum_o(fa8_sum_o[3:0]),
.fa4_carry_o(w_carry)
);
Adder_4bit U_A4_2(
.i_fa4_a(i_fa8_a[7:4]),
.i_fa4_b(i_fa8_b[7:4]),
.i_fa4_cin(w_carry),
.fa4_sum_o(fa8_sum_o[7:4]),
. fa4_carry_o(fa8_carry_o)
);
endmodule
module Adder_4bit(
input [3:0] i_fa4_a,
input [3:0] i_fa4_b,
input i_fa4_cin,
output [3:0] fa4_sum_o,
output fa4_carry_o
);
wire [3:0] carry_w;
// assign carry_w[0] = i_fa4_cin;
full_adder U_FA1 (
.i_fa_a(i_fa4_a[0]),
.i_fa_b(i_fa4_b[0]),
.i_cin(i_fa4_cin),
.fa_sum_o(fa4_sum_o[0]),
.fa_carry_o(carry_w[0])
);
full_adder U_FA2 (
.i_fa_a(i_fa4_a[1]),
.i_fa_b(i_fa4_b[1]),
.i_cin(carry_w[0]),
.fa_sum_o(fa4_sum_o[1]),
.fa_carry_o(carry_w[1])
);
full_adder U_FA3 (
.i_fa_a(i_fa4_a[2]),
.i_fa_b(i_fa4_b[2]),
.i_cin(carry_w[1]),
.fa_sum_o(fa4_sum_o[2]),
.fa_carry_o(carry_w[2])
);
full_adder U_FA4 (
.i_fa_a(i_fa4_a[3]),
.i_fa_b(i_fa4_b[3]),
.i_cin(carry_w[2]),
.fa_sum_o(fa4_sum_o[3]),
.fa_carry_o(carry_w[3])
);
assign fa4_carry_o = carry_w[3];
endmodule
// full adder
module full_adder (
input i_fa_a,
input i_fa_b,
input i_cin,
output fa_sum_o,
output fa_carry_o
);
wire sum1, carry1, sum2, carry2;
half_adder U_HA1 (
.i_a(i_fa_a),
.i_b(i_fa_b),
.ha_sum_o(sum1),
.ha_carry_o(carry1)
);
half_adder U_HA2 (
.i_a(sum1),
.i_b(i_cin),
.ha_sum_o(fa_sum_o),
.ha_carry_o(carry2)
);
assign fa_carry_o = carry1 | carry2;
endmodule
// half adder
module half_adder (
input i_a,
input i_b,
output ha_sum_o,
output ha_carry_o
);
// assign ha_sum_o = i_a ^ i_b;
// assign ha_carry_o = i_a & i_b;
xor (ha_sum_o, i_a, i_b);
and (ha_carry_o, i_a, i_b);
endmodule
🛠️ 0~9999 출력하는 범용 calculator
> 0.1초마다 1씩 숫자가 증가해 9999가 되면 다시 0으로 돌아가는 출력 <
9999까지 출력하려면 2^14 → 14bit가 필요하다.
또한 fnd에서 4자리수를 동시에 출력하기 위해 계속해서 빠르게 출력함으로써 잔상효과를 이용한다.
→ clk을 이용해 계속해서 출력되도록 한다.
우리가 사용하는 fpga 보드인 basys3 에서는 기본적으로 100MHz가 출력된다.
따라서 0.1초 간격으로 숫자가 올라가도록 하려면 100MHz를 분주하여 0.1초 단위의 clock 신호를 만들어주어야 한다.
- clk_divider (prescaler) 기본 구조
- Block diagram
- upCounter.v (10000진 카운터)
`timescale 1ns / 10ps
module top(
input i_clk,
input i_reset,
output [3:0] fndCom_o,
output [7:0] fndFont_o
);
wire [13:0] w_bcdData;
upCounter U_UCNT(
.i_ucnt_clk(i_clk),
.i_ucnt_reset(i_reset),
.ucnt_count_o(w_bcdData)
);
fnd_controller U_FND_con(
.i_con_clk(i_clk),
.i_con_reset(i_reset),
.i_con_bcdData(w_bcdData),
.con_fndCom_o(fndCom_o),
.con_segData_o(fndFont_o)
);
endmodule
module upCounter(
input i_ucnt_clk,
input i_ucnt_reset,
output [13:0] ucnt_count_o
);
wire w_clk;
clk_divider_10MHz U_clk_div_10MHz(
.i_div_clk(i_ucnt_clk),
.i_div_reset(i_ucnt_reset),
.div_clk_o(w_clk)
);
counter_10MHz U_cnt(
.i_cnt_clk(w_clk),
.i_cnt_reset(i_ucnt_reset),
.cnt_count_o(ucnt_count_o)
);
endmodule
module clk_divider_10MHz(
input i_div_clk,
input i_div_reset,
output div_clk_o
);
reg [23:0] r_counter;
reg r_clk;
assign div_clk_o = r_clk;
always @(posedge i_div_clk, posedge i_div_reset) begin
if (i_div_reset) begin
r_counter <= 0;
r_clk <= 1'b0;
end
else begin
if (r_counter == 10_000_000 -1) begin
r_counter <= 0;
r_clk <= 1'b1;
end
else begin
r_counter <= r_counter + 1;
r_clk <= 1'b0;
end
end
end
endmodule
module counter_10MHz(
input wire i_cnt_clk,
input wire i_cnt_reset,
output reg [13:0] cnt_count_o
);
always @(posedge i_cnt_clk, posedge i_cnt_reset) begin
if (i_cnt_reset) begin
cnt_count_o <= 0; // blocking assignment
end
else if (cnt_count_o == 9999) begin
cnt_count_o <= 0;
end
else begin
cnt_count_o <= cnt_count_o + 1;
end
end
endmodule
- fnd_controller.v
`timescale 1ns / 10ps
module fnd_controller (
input wire i_con_clk,
input wire i_con_reset,
input wire [13:0] i_con_bcdData,
output wire [3:0] con_fndCom_o,
output wire [7:0] con_segData_o
);
wire [3:0] w_dig1, w_dig10, w_dig100, w_dig1000, w_bcdData;
wire [1:0] w_selFnd;
wire w_clk;
clk_divider U_clk_div(
.i_div_clk(i_con_clk),
.i_div_reset(i_con_reset),
.div_clk_o(w_clk)
);
counter U_counter(
.i_cnt_clk(w_clk),
.i_cnt_reset(i_con_reset),
.cnt_count_o(w_selFnd)
);
decoder_2to4 U_2to4 (
.i_dec_x(w_selFnd),
.dec_y_o(con_fndCom_o)
);
digit_splitter U_dig_splitter(
.i_dig_dig(i_con_bcdData),
.dig_dig1_o(w_dig1),
.dig_dig10_o(w_dig10),
.dig_dig100_o(w_dig100),
.dig_dig1000_o(w_dig1000)
);
mux_41 U_mux_41 (
.i_mul_sel(w_selFnd),
.i_mul_x0(w_dig1),
.i_mul_x1(w_dig10),
.i_mul_x2(w_dig100),
.i_mul_x3(w_dig1000),
.mul_y_o(w_bcdData)
);
BCDtoseg_o_decoder U_BCDtoSEG (
.i_bcd(w_bcdData),
.seg_o(con_segData_o)
);
endmodule
module clk_divider(
input i_div_clk,
input i_div_reset,
output div_clk_o
);
reg [16:0] r_counter;
reg r_clk;
assign div_clk_o = r_clk;
always @(posedge i_div_clk, posedge i_div_reset) begin
if (i_div_reset) begin
r_counter <= 0;
r_clk <= 1'b0;
end
else begin
if (r_counter == 100_000 -1) begin
r_counter <= 0;
r_clk <= 1'b1;
end
else begin
r_counter <= r_counter + 1;
r_clk <= 1'b0;
end
end
end
endmodule
module counter(
input wire i_cnt_clk,
input wire i_cnt_reset,
output reg [1:0] cnt_count_o
);
always @(posedge i_cnt_clk, posedge i_cnt_reset) begin
if (i_cnt_reset) begin
cnt_count_o <= 0; // blocking assignment
end
else begin
cnt_count_o <= cnt_count_o + 1;
end
end
endmodule
// BCDtoseg_o_decoder
module BCDtoseg_o_decoder (
input wire [3:0] i_bcd,
output reg [7:0] seg_o
);
always @(i_bcd) begin
case (i_bcd)
4'h0: seg_o = 8'hc0;
4'h1: seg_o = 8'hf9;
4'h2: seg_o = 8'ha4;
4'h3: seg_o = 8'hb0;
4'h4: seg_o = 8'h99;
4'h5: seg_o = 8'h92;
4'h6: seg_o = 8'h82;
4'h7: seg_o = 8'hf8;
4'h8: seg_o = 8'h80;
4'h9: seg_o = 8'h90;
4'ha: seg_o = 8'h88;
4'hb: seg_o = 8'h83;
4'hc: seg_o = 8'hc6;
4'hd: seg_o = 8'ha1;
4'he: seg_o = 8'h86;
4'hf: seg_o = 8'h8e;
default: seg_o = 8'hff;
endcase
end
endmodule
// decoder_2to4
module decoder_2to4 (
input [1:0] i_dec_x,
output reg [3:0] dec_y_o
);
always @(i_dec_x) begin
case (i_dec_x)
2'b00: dec_y_o = 4'b1110;
2'b01: dec_y_o = 4'b1101;
2'b10: dec_y_o = 4'b1011;
2'b11: dec_y_o = 4'b0111;
default: dec_y_o = 4'b1111;
endcase
end
endmodule
module digit_splitter (
input wire [13:0] i_dig_dig,
output [3:0] dig_dig1_o,
output [3:0] dig_dig10_o,
output [3:0] dig_dig100_o,
output [3:0] dig_dig1000_o
);
assign dig_dig1_o = i_dig_dig % 10;
assign dig_dig10_o = (i_dig_dig / 10) % 10;
assign dig_dig100_o = (i_dig_dig / 100) % 10;
assign dig_dig1000_o = (i_dig_dig / 1000) % 10;
endmodule
module mux_41 (
input wire [1:0] i_mul_sel,
input wire [3:0] i_mul_x0,
input wire [3:0] i_mul_x1,
input wire [3:0] i_mul_x2,
input wire [3:0] i_mul_x3,
output reg [3:0] mul_y_o
);
always @(*) begin
case (i_mul_sel)
2'b00: mul_y_o = i_mul_x0;
2'b01: mul_y_o = i_mul_x1;
2'b10: mul_y_o = i_mul_x2;
2'b11: mul_y_o = i_mul_x3;
default: mul_y_o = 4'bx;
endcase
end
endmodule
- RTL Analysis
🛠️ sw0 : run/stop, sw1 : clear → counter 제어 시스템
- Block diagram
아래 코드의 경우 0.1초 간격으로 숫자가 올라가기 때문에 clock에 따른 타이밍 오류가 발생하지 않는 것처럼 보인다.
그러나 사실 만약 0.1초가 아니라 10초 간격으로 clk이 분주된 경우에는 타이밍 오류가 발생할 수 있다.
(해결 다음 포스팅에서 후술)
- top.v
`timescale 1ns / 10ps
module top(
input i_clk,
input i_reset,
input i_runStop,
input i_clear,
// input [1:0] i_sw, // 00 : run, 01 : stop, 11: clear
output [3:0] fndCom_o,
output [7:0] fndFont_o
);
wire [13:0] w_bcdData;
upCounter U_UCNT(
.i_ucnt_clk(i_clk),
.i_ucnt_reset(i_reset),
.i_ucnt_runStop(i_runStop),
.i_ucnt_clear(i_clear),
.ucnt_count_o(w_bcdData)
);
fnd_controller U_FND_con(
.i_con_clk(i_clk),
.i_con_reset(i_reset),
.i_con_bcdData(w_bcdData),
.con_fndCom_o(fndCom_o),
.con_segData_o(fndFont_o)
);
endmodule
module upCounter(
input i_ucnt_clk,
input i_ucnt_reset,
input i_ucnt_runStop,
input i_ucnt_clear,
output [13:0] ucnt_count_o
);
wire w_clk;
clk_divider_10MHz U_clk_div_10MHz(
.i_div_clk(i_ucnt_clk),
.i_div_reset(i_ucnt_reset),
.div_clk_o(w_clk)
);
counter_10MHz U_cnt(
.i_cnt_clk(w_clk),
.i_cnt_reset(i_ucnt_reset),
.i_cnt_runStop(i_ucnt_runStop),
.i_cnt_clear(i_ucnt_clear),
.cnt_count_o(ucnt_count_o)
);
endmodule
module clk_divider_10MHz(
input i_div_clk,
input i_div_reset,
output div_clk_o
);
reg [23:0] r_counter;
reg r_clk;
assign div_clk_o = r_clk;
always @(posedge i_div_clk, posedge i_div_reset) begin
if (i_div_reset) begin
r_counter <= 0;
r_clk <= 1'b0;
end else begin
if (r_counter == 10_000_000 - 1) begin
r_counter <= 0;
r_clk <= ~r_clk;
end else begin
r_counter <= r_counter + 1;
r_clk <= 1'b0;
end
end
end
endmodule
module counter_10MHz(
input wire i_cnt_clk,
input wire i_cnt_reset,
input wire i_cnt_runStop,
input wire i_cnt_clear,
output reg [13:0] cnt_count_o
);
always @(posedge i_cnt_clk, posedge i_cnt_reset) begin
if (i_cnt_reset) begin
cnt_count_o <= 0; // blocking assignment
end else if (i_cnt_runStop == 1) begin
if (i_cnt_clear == 1) begin
cnt_count_o <= 0;
end
end else begin
if (cnt_count_o == 9999) begin
cnt_count_o <= 0;
end else begin
cnt_count_o <= cnt_count_o + 1;
end
end
end
endmodule
- fnd_controller.v의 경우 위의 코드와 동일함 ( 포스팅 길이 관계로 생략)
'하만 세미콘 아카데미 8기 > verilog 설계' 카테고리의 다른 글
241206 cpu 설계 기초 2 (0) | 2024.12.07 |
---|---|
241205 cpu 설계 기초 (0) | 2024.12.06 |
241104 verilog 기초 1 (+gate, adder) (0) | 2024.11.07 |
241105 verilog 기초 2 (+fnd controller) (0) | 2024.11.07 |