[Schematic]
[parameter]
이전에 4bit, 12bit counter를 만들었는데,
parameter를 활용하면 굳이 bit마다 모듈을 만들어줄 필요가 없다.
[Bin_to_Dec]
아무래도 알파벳으로 카운팅되면, 가독성이 떨어질 수 밖에 없다.
∴ 0123456789abcdef → 0123456789
이전에 만들었던, bin_to_dec 모듈을 counter에 인스턴스하면 된다.
[Toggle]
버튼 누를 때마다, up/down 토글되도록 만들어 보자.
→ TFF 활용.
※ 이렇게 하면 에러 발생
clk 입력에 버튼 입력을 주니까 에러가 생김
clk 입력에는 FPGA 보드의 정규 clk(125MHz)만 사용해야 한다.
다른 걸 넣는다는 것은 비동기로 쓴다는 의미
→ 동기화 문제가 발생한다.
ex) clk_div, clk_1ms 등 다 비정규 clk
→ clk_div 같은 정규 clk를 받아서 쓰는 것은 그나마 사용 가능하다.(pdt가 별로 안 생김)
버튼 입력같이 무관한 clk를 쓰는 경우에 문제 발생
[Edge detector]
Edge_detector는 비정규 clk로 들어온 입력(Async)을 받아
정규 클락을 기준으로 하는 Pulse를 만들어 그 것을 신호로 사용한다,
따라서 위와 같이 비정규 clk를 사용했을 때 발생하는 동기화 오류를 해결하기 위해서는
버튼 입력을 clk로 쓰는 대신, edge detector 회로를 추가해주면 된다.
✅ Pull-up, Pull-down 저항과의 연관성
① Pull-up 저항을 사용할 경우, 평소에는 High(1)의 입력이 들어오고 있다가
스위치가 동작하면 Low(0)가 입력된다.
해당 신호를 Edge-detector로 받을 경우, neg_edge 신호가 출력되므로
Pull-up 저항을 사용할 때는 Edg의 neg_edge 출력을 선택해서 사용해야 한다.
② Pull-down 저항을 사용할 경우, 평소에는 Low(0)가 입력되고 있다가
스위치가 동작하면 High(1)가 입력된다.
해당 신호를 Edge_detector로 받을 경우, pos_edge 신호가 출력되므로
Pull-down 저항을 사용할 때는 Edg의 pos_edge 출력을 선택하여 사용해야 한다.
※ blocking / non-blocking
현재 작업이 block(차단, 대기) 되느냐 아니냐에 따라 다른 작업을 수행할 수 있는지
다른 요청의 작업을 처리하기 위해 현재 작업을 block(차단, 대기) 하냐 안하냐의 유무
파일을 읽는 작업이 있을 때, 블로킹 방식으로 읽으면 파일을 다 읽을 때까지 대기하고, 논블로킹 방식으로 읽으면 파일을 다 읽지 않아도 다른 작업을 할 수 있다.
////Blocking////
always @ * begin
cp_in = 1;
cp_cur = cp_in;
cp_old = cp_cur; /// line5에서 계산된 cp_cur이 적용됨.
end
//// cp_in = cp_cur = cp_old = 1
//// 이전 라인이 다음 라인에 각각 반영되면서 나감.
////Non-blocking////
always @* begin
cp_in = 1;
cp_cur <= cp_in;
cp_old <= cp_cur; //// 초기 cp_cur 값이 0이라고 하면,
//// line5 에서의 cp_cur이 반영되지 않았으니
//// cp_old = 0
end
//// cp_in = 1, cp_cur = 1, cp_old = 0
//// 즉, 병렬적으로 각각 동작한다.
그냥 순차 논리 회로에서는 무조건 Non-blocking을 사용하는 것이 안전하다.
위와 같이 counter_fnd_top에 edge dector를 instance해주고 디텍터의 출력을 TFF에 넣어주면, 동기화 에러가 해결된다.
[debouncing]
보통 bouncing은 1ms 이내에 끝난다. → 그걸 기다려주면 됨.
1ms에 한 번씩 clk가 바뀌는 DFF에 연결해주면 바운싱 제거됨.
cf) 다른 방식의 동기화/디바운싱
module debounce_better_version(input pb_1,clk,output pb_out);
wire slow_clk_en;
wire Q1,Q2,Q2_bar,Q0;
clock_enable u1(clk,slow_clk_en);
my_dff_en d0(clk,slow_clk_en,pb_1,Q0); // -----> debouncing
//////////////////////////////////////////////////
my_dff_en d1(clk,slow_clk_en,Q0,Q1); //
my_dff_en d2(clk,slow_clk_en,Q1,Q2); //
assign Q2_bar = ~Q2; //
assign pb_out = Q1 & Q2_bar; // ---> edge detector
//////////////////////////////////////////////////
endmodule
//// edge detector는 어짜피 DFF 두 개로 만들어졌으니,
//// 한 모듈로 만들 수 있다.
<Edge _detector 모듈 없는 코딩>
module decoder_7seg( //// 적은 bit의 데이터를 받아 많은 bit로 출력
input [3:0] hex_value,
output reg [7:0] seg_7
);
always @ (hex_value) begin
case(hex_value)
4'b0000 : seg_7 = 8'b0000_0011;
4'b0001 : seg_7 = 8'b1001_1111;
4'b0010 : seg_7 = 8'b0010_0101;
4'b0011 : seg_7 = 8'b0000_1101;
4'b0100 : seg_7 = 8'b1001_1001;
4'b0101 : seg_7 = 8'b0100_1001;
4'b0110 : seg_7 = 8'b0100_0001;
4'b0111 : seg_7 = 8'b0001_1111;
4'b1000 : seg_7 = 8'b0000_0001;
4'b1001 : seg_7 = 8'b0000_1001;
4'b1010 : seg_7 = 8'b0001_0001;
4'b1011 : seg_7 = 8'b1100_0001;
4'b1100 : seg_7 = 8'b0110_0011;
4'b1101 : seg_7 = 8'b1000_0101;
4'b1110 : seg_7 = 8'b0110_0001;
4'b1111 : seg_7 = 8'b0111_0001;
endcase
end
endmodule
module bin_to_dec(
input [11:0] bin,
output reg [15:0] bcd
);
reg [3:0] i;
always @(bin) begin
bcd = 0;
for (i=0 ; i< 12 ; i = i+1) begin
bcd = {bcd[14:0], bin[11-i]};
if(i<11 && bcd[3:0] >4) bcd[3:0] = bcd[3:0] + 3;
if(i<11 && bcd[7:4] >4) bcd[7:4] = bcd[7:4] + 3;
if(i<11 && bcd[11:8] >4) bcd[11:8] = bcd[11:8] + 3;
if(i<11 && bcd[15:12] >4) bcd[15:12] = bcd[15:12] + 3;
/// 100보다 큰 것을 좌쉬프트하면 1010을 넘어가는데,
/// BCD에서는 사용 불가하니까 건너뛰어야함.
/// 쉬프트 전 3을 더해주는 것 = 쉬프트 후 6 더하는 것
end
end
endmodule
module up_down_counter_Nbit #(parameter N=4)(
input clk, rst, up_down,
output reg [N-1:0] count
);
always @(posedge clk, posedge rst) begin
if(rst) count = 0;
else begin
if(up_down) count = count + 1;
else count = count - 1;
end
end
endmodule
module ring_count_fnd( //// 0001 0010 0100 1000 순환하는 링카운터
input clk,
output reg [3:0] com
);
always @(posedge clk) begin
if(com != 4'b1110 && com != 4'b1101 &&
com != 4'b1011 && com != 4'b0111) com = 4'b1110;
else if (com == 4'b0111) com = 4'b1110;
else com = {com[2:0], 1'b1};
end
endmodule
module FND_4digit_cntr(
input clk, rst,
input [15:0] count,
output [3:0] com,
output [7:0] seg_7
);
reg [16:0] clk_1ms;
always @ (posedge clk) clk_1ms = clk_1ms + 1;
/// ringcounter를 위한 분주기
ring_count_fnd ring(.clk(clk_1ms[16]), .com(com));
reg [3:0] hex_value;
wire [7:0] seg_7_font;
decoder_7seg dec(.hex_value(hex_value), .seg_7(seg_7_font));
assign seg_7 = ~seg_7_font;
always @ (negedge clk) begin
case(com)
4'b1110 : hex_value = count[15:12];
4'b1101 : hex_value = count[11:8];
4'b1011 : hex_value = count[7:4];
4'b0111 : hex_value = count[3:0];
endcase
end
endmodule
module counter_fnd_top(
input clk, rst, btn1,
output [7:0] seg_7,
output [3:0] com
);
reg [25:0] clk_div;
always @ (posedge clk) clk_div = clk_div + 1;
D_flip_flop_n debnc(.d(btn1), .clk(clk_div[14]),
.rst(rst), .q(up_down));
/// ----------> debouncing
////////////////////////////////////////////////////////////////////
wire cp_cur, cp_old, p_edge;
D_flip_flop_n edge1(.d(up_down), .clk(clk), .rst(rst), .q(cp_cur));
D_flip_flop_n edge2(.d(cp_cur), .clk(clk), .rst(rst), .q(cp_old));
wire old_bar;
assign old_bar = ~cp_old;
assign p_edge = cp_cur & old_bar;
// wire cur_bar;
// assign cur_bar = ~cp_cur;
// assign n_edge = cur_bar & cp_old; // n_edge 출력을 원할 때 사용
///////////////////////////////////////////////////////////////////
///--------> edge detector
wire up;
t_flip_flop t_up(.clk(clk), .rst(rst), .t(p_edge), .q(up));
wire [11:0] count;
up_down_counter_Nbit #(.N(12)) counter_fnd(.clk(clk_div[25]),
.rst(rst), .up_down(~up), .count(count));
wire [15:0] dec_value;
bin_to_dec bin2dec(.bin(count), .bcd(dec_value));
FND_4digit_cntr cntr(.clk(clk), .rst(rst), .count(dec_value),
.com(com), .seg_7(seg_7));
endmodule
module D_flip_flop_n(
input d, clk, rst,
output reg q
);
always @(posedge clk) begin
if (rst) q = 0;
else q = d;
end
endmodule
module t_flip_flop(
input clk, rst, t,
output reg q
);
always @ (posedge clk, posedge rst) begin
if(rst) q = 0;
else if(t) q = ~q;
else q = q;
end
endmodule
※ 새로 받은 Bread board로 이전하여 code 변경됨
module D_flip_flop_n(
input d,
input clk,rst,
output reg q
);
always @(negedge clk, posedge rst) begin
/// edge trigering
if(rst) q = 0;
///// FF는 리셋 기능이 꼭 있어야 한다.
else q = d;
end
module t_flip_flop_p(
input clk, rst,
input t, /// t = cp_in으로 보면 됨.
output reg q
);
always@(posedge clk, posedge rst) begin
if(rst) q=0;
else if (t) q = ~q;
else q = q;
//// 사실 FF(순차논리회로)에서는 굳이 안써줘도 됨.
//// 어짜피 값을 유지하기 때문에.
//// 조합논리회로에서는 꼭 써줘야 한다.
//// 안 해주면 LATCH가 만들어짐.
end
endmodule
module bin_to_dec(
//// 12bit binary를 받아서 16bit decimal로 변환
input [11:0] bin,
output reg [15:0] bcd
/// 올림수가 하나만 나와도 4자리 다 읽어야 해서 vector : 16
);
reg [3:0] i;
always @(bin) begin
//// FND 출력할 때 자주 써먹게 될 것임.
bcd = 0;
for (i=0 ; i<12 ; i=i+1) begin
bcd = {bcd[14:0], bin[11-i]};
//// 쉬프트 연산을 결합연산자로 표현(회로가 더 간단해진다.)
//// 좌로 1bit 쉬프트하고, 빈 자리에는 bin[11-i]를 넣어 준다.
if ( i < 11 && bcd[3:0] > 4) bcd[3:0] = bcd[3:0] + 3;
if ( i < 11 && bcd[7:4] > 4) bcd[7:4] = bcd[7:4] + 3;
if ( i < 11 && bcd[11:8] > 4) bcd[11:8] = bcd[11:8] + 3;
if ( i < 11 && bcd[15:12] > 4) bcd[15:12] = bcd[15:12] + 3;
end
end
endmodule
module ring_count_fnd( //// 라운드 로그인 방식
input clk,
output [3:0] com
);
reg [3:0] temp; //// 초기값을 정해줘야 하므로 temp 선언
always @ (posedge clk) begin
if(temp != 4'b1110 && temp != 4'b1101
&& temp != 4'b1011 && temp != 4'b0111)
temp = 4'b1110; /// 초기값은 0001
/// 0줬을 때 켜지는 FND는 4'b1110
else if (temp == 4'b0111) temp = 4'b1110;
//// 1000되면 0001로 돌아와라
else temp = {temp[2:0], 1'b1};
//// 결합연산자 이용하여 좌시프트
end //// 0001 0010 0100 1000 을 반복하는 링카운터
assign com = temp;
//// temp를 다시 com이라는 출력에 연결시켜준다.
endmodule
module FND_4digit_cntr(
//// ring counter top module 생성 ---- 16bit의 값을 받아서 1ms 간격으로 한 자리씩 켜준다.
input clk, rst,
input [15:0] value, //// 4자리니까 16bit
output [3:0] com,
output [7:0] seg_7
);
reg [16:0] clk_1ms;
//// 8ns * 2^17 = 1,048,576ns = 1048㎲ = 1.048ms
always @(posedge clk) clk_1ms = clk_1ms + 1 ;
ring_count_fnd ring1(.clk(clk_1ms[16]), .com(com));
//// 1ms posedge마다 좌시프트
//// 1ms 마다 CT 순대로 LED가 점멸한다.
wire [7:0] seg_7_font;
reg [3:0] hex_value;
decoder_7seg ring_seg_7(.hex_value(hex_value), .seg_7(seg_7_font));
assign seg_7 = ~seg_7_font;
always @(negedge clk) begin
//// 카운팅은 posedge에 했으니, 겹치지 않게 negedge
case(com)
4'b1110 : hex_value = value[15:12];
//// CT1에 해당하는 hex_value는 value[15:12]
4'b1101 : hex_value = value[11:8];
4'b1011 : hex_value = value[7:4];
4'b0111 : hex_value = value[3:0];
endcase
//// edge triggering은 default에서 현재 값을 유지해도 FF가 만들어지니 상관없다.
end
endmodule
module up_down_counter_Nbit #(parameter N = 4)(
//// Nbit 업다운 카운터
input clk, rst, up_down,
output reg [N-1:0] count
);
always @(posedge clk, posedge rst) begin
if(rst) count = 0;
else begin
if(up_down)
count = count + 1;
else
count = count - 1;
end
end
endmodule
module counter_fnd_top(
//// count와 fnd만 묶어서 top module 생성
input clk, rst, btn1,
output [7:0] seg_7, // top module을 통한 출력
output [3:0] com
);
wire [11:0] count;
/// 카운터의 출력은 wire로 디코더와 연결
// assign com = 4'b1111;
//// 나타낼 부분은 1
reg [25:0] clk_div;
always @(posedge clk)
clk_div = clk_div + 1;
///// 분주기 만들어줌(시뮬레이션은 분주기가 필요없지만,
///// 직접 출력할 때는 분주기가 필요해)
///// 약 0.5초마다 count가 증가한다.
///// (보드의 주기 : 8ns) → 8ns x 2^26 = 0.56 sec
D_flip_flop_n debnc1(.d(btn1), .clk(clk_div[14]), .rst(rst), .q(up_down));
//// 위의 clk divider의 16번쨰 bit를 써야 된다.
wire up_down_p;
edge_detector_n(.clk(clk), .cp_in(up_down), .rst(rst), .p_edge(up_down_p));
/// 안 쓰는 출력은 빼버리면 된다.
wire up;
t_flip_flop_p t_up(.clk(clk), .t(up_down_p), .rst(rst), .q(up));
//// 버튼 누를때마다 up/down 토글
up_down_counter_Nbit #(.N(12)) counter_fnd
(.clk(clk_div[25]), .rst(rst), .up_down(~up), .count(count));
//// clk는 125MHz로 너무 빠르다. ---> 분주기 만들어줘야 됨.
wire [15:0] dec_value;
bin_to_dec bin2dec(.bin(count), .bcd(dec_value));
//// 12bit binary를 받아서 16bit decimal로 변환
FND_4digit_cntr f_dgt_cntr(.clk(clk), .rst(rst), .value({dec_value}),
.com(com), .seg_7(seg_7));
/// 컨트롤러가 분주기 가지고 있으니 그냥 clk준다.
endmodule
module edge_detector_n( /// falling edge detector
input clk, cp_in, rst, /// cp : clock pulse
output p_edge, n_edge /// 아주 짧은 펄스 하나를 내보냄.
);
reg cp_in_old, cp_in_cur;
always @ (negedge clk, posedge rst) begin
if(rst) begin
cp_in_old = 0;
cp_in_cur = 0;
end
else begin
cp_in_old <= cp_in_cur;
cp_in_cur <= cp_in;
/// <= : 대입 연산자(=과 유사한 기능)
/// non-blocking 값이 들어가는 순서가 있을 때는 무조건 non-blocking
/// 순차논리회로에서는 non-blocking을 무조건 쓰는 것이 안전하다.
/// 조합논리회로(level triggering)에서는 무조건 blocking
/// 블록 내에 한 줄일때는 아무거나 써도 상관없어.
end
end
assign p_edge = ~cp_in_old & cp_in_cur;
//// cp_old는 아직 0이니까 반전
assign n_edge = cp_in_old & ~cp_in_cur;
//// cp_cur는 먼저 0이 되었으므로 반전시켜줌
endmodule
<리셋값 조정 & up_count로 시작>
이전에는 리셋하면 0 _ _ _ 이렇게 출력되었음.
코드 추가 후 0000 출력
<버튼을 뗄 때 up/down 변환>
'# Semiconductor > - Semicon Academy' 카테고리의 다른 글
[Harman 세미콘 아카데미] 11일차 - 반도체 개요, C언어 개요 및 문법 (0) | 2023.07.03 |
---|---|
[Harman 세미콘 아카데미] 10일차 - Register, Memory (0) | 2023.06.30 |
[Harman 세미콘 아카데미] 8일차 - review, async/sync counter (0) | 2023.06.28 |
[Harman 세미콘 아카데미] 6~7일차 - 취업 특강 (0) | 2023.06.26 |
[Harman 세미콘 아카데미] 5일차 - Sequential Logic(LATCH, FF) (0) | 2023.06.23 |