Verilog_FND Timer(Basys-3)_07.17

공이지·2024년 7월 18일
post-thumbnail

아직 포스트하지 못한 clock카운터를 정리하고 나서 정리해야하는 내용이지만 오늘 배운 내용은 바로 정리가 필요할거같아서 미리 하겠습니다.

  • 시간(초) 단위.

FND 링카운터

  • 우리가 실습보드에 4개의 FND를 사용할 것이다. 링카운터를 사용해서 1ms 주기로 FND의 com단자를 반복하여 ON시키면 1ms라는 시간 주기로 돌아 COM단자가 순서대로 켜지는데 1ms라는 시간은 우리눈에는 4개의 COM단자 전체가 계속 켜져있는 모습을 보여줄 것이다.
  • 설명 :10ns의 주기 계산은 reg [17:0] clk_div; ,always@(posedge clk)clk_div = clk_div+1; 다음코드에서 보면 clk에 1씩 더하게 된다면 한번 더할 때마다 1을 더한 주기는 이전 주기의 2배이다. 즉 +1을 한 주기는 이전 주기 2개분의 주기를 갖는다는 의미! 그래서 이를 2의 제곱을 하여 10을 곱해주면 된다. 만약 2^10을 하고 10ns이므로 10을곱하면 대략 10us의 값이 나온다!
  • reg [20:0] clk_div; 이 코드에서 이렇게 clk_div를 0으로 초기화하지않으면 위 시뮬레이션처럼 clk값을 받지못합니다. 물론 시뮬레이션을 진행할때만!!! 보드에서 실습할때는 상관없음!

보드 실습코드 및 실행결과

링카운터 FND

  • 원리설명 :링카운터로 clk의 주기를 맞춰주고 1ms마다 4개의com단자. 즉, 4비트의 com단자의 첫번째 비트부터 0을 입력하여 쉬프트 연산을 하여 첫번째 비트부터 4번째 비트까지 10ms의 시간마다 FND가 on되도록 합니다. 10ms라는 시간은 우리가 눈으로 볼때는 계속 켜져있는것처럼 보일 것입니다. 이후 우리가 링카운터에서 출력으로 선언한 com의 출력값을 case문으로 사용하여 reg로 선언한 hex_value값에 16개의 스위치를 4개로 나누면 4비트로 만들수있는 16진수 값을 4개의 FND. 즉 4개의 com단자에 표현할 수 있습니다. 0~3번까지 1의자리수를 표현하고 4~7은 십의자리, 8~11은 백의자리, 12~15는 천의자리수를 표현합니다. 간단하게 말하자면 0~3번째 스위치중 0번 스위치만 on하면 0001 이 되어 com단자가 1110 이라면. 즉 일의자리수 FND에 1이 표현됩니다. 마지막에는 value(switch)값이 0001이 주어진다면 이 값이 hex_value에 할당되고 이 hexvalue는 decoder_7seg의 hex_value로 들어가게되면 디코더의 hex_value값에 따라 basys-3-Master.xdc에 선언된 8비트 seg_78'b1001_1111 값이 들어가서 1 이라는 숫자가 표현됩니다!!!

FND Timer

  • 원리설명 : clock_div_100clock_div_1000으로 ns의 주기를 가진 Basys-3 보드의 주기를 초단위로 카운트하여 타이머의 시간을 계산합니다. 그리고 계산한 초를 받아서 counter_BCD_60의 입력으로 설정하여 초, 분은 모두 0~60을 순회하므로 0~60을 반복하는 카운터입니다. us->s로 변환한 카운트를 BCD카운터에 입력하고 여기에서 또 엣지 디텍터에 들어갑니다. 이는 us에서 s로 변환 될때마다 BCD카운터로 변환된 값이 들어갈때마다 엣지디텍터의 원사이클 펄스가 발생되고 이를 발생하면 1의자리 BCD가 올라가고 1의자리는 0~9까지이므로 9를넘어가면 0으로 변하게되며, 10의자리는 5를 넘어가면 0이되도록 구현해줬습니다. 그리고 min이 올라가는 원리도 이와같지만 60주기 cnt_clksource에 들어가는 초단위 카운트가 30이상이면 clk_div_60이 1이되고 30이하면 clk_div_60이 0이 될 것이다. 이렇게되면 사진에서 counter_BCD_60clk_min이 들어가게되는데 여기에도 edge디텍터의 .cp로 들어가게되면서 하강엣지가 되면, 즉, 하강엣지가 되는 시점이 cnt_clksourc이 30에서 0으로가는 시점이다. 이는 60초가 카운트 되었을때 counter_BCD_60의 edge디텍터에서 하강엣지가 발생하여 분이 올라간다!!! 분이 상승하는 조건문은 else if(clk_time_negedge)begin을 통해서 정해진다! 업로드중..

FND Timer Set/Watch Mode


원리 및 코드 설명

  • 처음에 watch모드(1)로 시작하다가 버튼을 누르면 set모드(0)로 변경합니다. T F/F을 사용해서 2개의 모드가 버튼을 눌렀을때 토글되는 방식입니다. 플립플롭의 출력으로는 set_watch를 연결하여 watch모드라면 clk을 받아 Timer가 동작하고 set모드라면 버튼을 통해서 Timer를 설정하도록 합니다.
  • 상승엣지에서. 즉, 버튼을 눌렀을때 edge detector가 원사이클 펄스를 감지하여 counter_BCD_60 입력 clk_time에 들어가면 또 bcd카운터에 있는 edge dectector의 입력으로 들어가면서 버튼을 누를 때마다 하나씩 카운트되면서 값이 올라가게된다!
  • 전체 코드 버튼 타이머 기능 설명 : clock_div_100clock_div_1000으로 ns의 주기를 가진 Basys-3 보드의 주기를 초단위로 카운트하여 타이머의 시간을 계산합니다. 그리고 계산한 초를 받아서 counter_BCD_60의 입력으로 설정하여 초, 분은 모두 0~60을 순회하므로 0~60을 반복하는 카운터입니다. us->s로 변환한 카운트를 BCD카운터에 입력하고 여기에서 또 엣지 디텍터에 들어갑니다. 이는 us에서 s로 변환 될때마다 BCD카운터로 변환된 값이 들어갈때마다 엣지디텍터의 원사이클 펄스가 발생되고 이를 발생하면 1의자리 BCD가 올라가고 1의자리는 0~9까지이므로 9를넘어가면 0으로 변하게되며, 10의자리는 5를 넘어가면 0이되도록 구현해줬습니다. 그리고 T F/F을 사용해서 버튼을 눌렀을때 타이머, set모드를 설정하도록 하였습니다. set_watch에 0이 들어가면 타이머, 1이 들어가면 set모드가 되도록 하였고, 초기화를하면set_watch가 1로 설정됩니다! 그리고 mux =>(assign value = {min10,min1, sec10,sec1};)를 사용하면 set_watch에 따라서 btn[1]을 누르면 edge_dectector_n btn1 버튼 1의 엣지 디텍터가 버튼을 눌렀을때 원사이클 펄스를 발생하여 출력btn_sec가 카운트되어 누를때 1씩 증가합니다! 분도 이와 같은 원리로 동작합니다!

FND Timer Set/Watch Mode의 문제점보완

  • 1번째 문제 사진의 clock_div_60.clk_sourceclk_sec를 받습니다. 이렇게 되면 watch모드에서 setmode로 바뀔때 clk_sec는 멈추지 않고 계속 카운트를 할 것입니다. 이렇게 되면 setmode에서 초를 40초로 설정하고 20초가 지나도watch모드에서 초를 카운트하는 clk_sec는 40초가 아니므로 초를 나타내는 FND에 60이 넘어가도 분이 바뀌지 않음을 확인할 수 있습니다. 그러면 clock_div_60(.clk(clk), .reset_p(reset_p), .clk_source(inc_sec), .clk_div_60(clk_min)); 다음과 같이 clk_sourceinc_sec으로 변경하면 clk_source주기가 모드에 따라서 변경이되고 값이 들어가기 때문에 setmode에서 초를 변경하고 watch모드로 변경해도 60초가 지나서 분이 변경되는것을 확인할 수 있습니다.
  • 2번째 문제30초 이상일때 watch모드에서 setmode로 바꾸게되면 clk_div_60에서 assign clk_div_60 = (cnt_clksource < 30) ? 0:1; 이 코드의 결과로 인해서 이 코드의 assign inc_sec = set_watch ? btn_sec : clk_sec; 다음 inc_sec이 1이될 것이다. 왜?clock_div_60 min_clk(.clk(clk), .reset_p(reset_p), .clk_source(inc_sec), .clk_div_60(clk_min)); 여기에서 clk_sourceinc_sec가 들어가기 때문이다. watch모드에서 setmode로 변경하게되면 inc_sec가 1에서 0으로 된다. 이유는 30초 이상에서는 1이였던 clk_secsetmode로 전환되면 버튼을 누르지 않는 한 btn_sec가 0이므로 이때 하강엣지가 발생한다. 그렇게되면 inc_secclk_time으로 받는 counter_BCD_60 counter_min 이곳에서 하강엣지가 발생하면서 min 카운트 값이 올라가게될 것이다. 이럴때 해결방안은 assign clk_div_60 = (cnt_clksource < 30) ? 0:1; 다음에서 30 이상일때 1이되는 기준을 높여서 59를 넘을때 1로 설정하면 60초가될때 하강엣지가 발생할 것이다. 하지만 setmode에서 59초를 설정하면 문제는 또 다시 발생할 것이다. 이를 해결하기위해서는 clock_div_60 min_clk(.clk(clk), .reset_p(reset_p), .clk_source(inc_sec), .clk_div_60_negedge(clk_min)); 다음과 같이 clk_div_60_negedge로 변경해주면 된다. clock_div_60 여기에선 출력 clock_div_60는 60주기를 카운트 할때 60카운트 하기위해서 30초 이하에선 0을 출력하고 30 이상에서는 1을출력하여 값을 출력합니다. 그렇게 되면 watch에서 set으로 모드가 바뀔때 하강엣지가 발생하면서 min 카운트값이 상승할 것이다. 하지만 clk_div_60_negedge으로 변경해주면 항상 카운트가 59초 이후 원사이클펄스가 발생되고 min의 카운트 값이 상승하기 때문에 이전의 문제를 해결할 수 있다. 하지만 이렇게되면 기존에는 clk_div_60를 사용할때는 모듈에서 2개의 엣지디텍터를 사용하면서 첫번째 디텍터에서 n_edge를 사용하고 그 원사이클 펄스를 사용해서 카운트한 값으로 두번째 엣지디텍터의 입력으로 사용되는데 이전에는 이 두번째 엣지디텍터의 입력을 clk_min과 연결했다면 clk_div_60_negedge를 사용하므로 두번째 디텍터의 원사이클 펄스가 발생했을때 min카운트 값을 상승한다. 이때 원사이클은 펄스는 cp 입력의 한사이클 차이가 발생할때 한주기 이후에두번째 엣지디텍터의 원사이클 펄스가 발생하는데 이렇게되면 원사이클 펄스가 2번 발생하므로 2주기가 우리가 원하는 시간보다 2주기가 밀려서 나오게 된다. 하지만 이는 큰 문제가 되지 않는다!!
  • 3번째 문제버튼을 누르면 채터링현상이 일어나므로 딜레이를 줘야한다. verilog에선 D F/F을 사용해서 버튼에 딜레이를 줍니다. 상승엣지에서 버튼을 누르면 채터링 현상은 보통 1ms 이후 사라지기 때문에 버튼을 누를때는 1이라고 하면 1ms이후 계속 1일 것이다. 쉽게 말하면 D F/F으로 채터링현상으로 1과 0으로 튀는 버튼의 값을 D F/F의 입력으로 받고 D F/F의 clk을 1ms로 하면 1ms 간격으로 상승엣지가 발생하므로 버튼을 누를 때 발생하는 채터링현상을 건너뛰고 값을 받을 수 있다 1ms의 주기를 만들어주는 곳은 첫번째 edge detector이다 클럭의 16번째 배열을 .cp로 받게되면 2^16 * 10ns을 계산하면 대략 1ms의 주기를 갖게된다.그렇게 되면 버튼이 누를때 2번째 edge detector cp입력이 들어가게되면서 상승엣지가 발생할때 원사이클 펄스가 발생할 것이다. 그러면 상승엣지를 받아서 버튼의 기능을 실행하게됩니다.

최종 코드 및 회로도


profile
화이팅..!

0개의 댓글