[Code] Xilinx + Vivado + digital clock (hours, minutes and seconds) + LED indication

[Code][Xilinx + Vivado + Digital Clock (Hours, Minutes and Seconds) + LED Indication] Practice Project

Final effect (no picture)

  • The count is accumulated once every sec every 1 second and is cleared after 59 times;
  • The min count accumulates once every 1 minute and is cleared after 59 times;
  • The hour count accumulates once every 1 hour and is cleared after 23 times;
  • LED1 flips alternately, once every second, so 1 on and 1 off is 2 seconds;
  • LED2 flips alternately, once every minute;

Related screenshots

Process Record

First of all, I want to make it clear that the current stage of entry into FPGA is very special. All engineering codes and engineering logic may have errors. I hope all students and teachers can point them out. At the same time, I also refer to a large number of high-quality content on the Internet. Thank you here. Based on This reason also decided to share the core logic. Please do not use it for any commercial purposes.

OK, let’s go

The first thing is to clarify the idea. We design a clock, which is a clock in our real world. A tick of 1 second is 1 second, not the 1 second specified by FPGA. The key to how to correspond 1 second in the FPGA to 1 second in the real world is to match the clock and count value of the FPGA. If our current FPGA clock is 50MHz (6th power), then the FPGA counts once is 20ns. We need to calculate 50_000_000 times to reach the real world ticking of 1 second. Effect. Note that the two 1 seconds are synchronized here, because both the real world and the FPGA consume 1 second. No matter which frame you start from, after matching from this frame, every 1 second thereafter (As long as the FPGA does not lose power and the world does not end) In theory, it can always match. This is the principle why digital clocks can display real-world time. Of course, the FPGA does not know what the current Beijing time is (the time correction function can be added), nor does it know that there are 60 minutes in an hour and 60 seconds in a minute. These logics are all implemented by ourselves.

Design module 1: The frequency division part cooperates with the clock and count value of the FPGA to achieve 1 second timing;
Design module 2: The second part realizes the accumulation of seconds, and the value is automatically cleared when it reaches 59;
Design module 3: The minute part realizes the accumulation of minutes, and the value is automatically cleared when it reaches 59;
Design module 4: The clock part realizes the accumulation of clocks, and the value is automatically cleared when it reaches 23;

Since we define sec, min, and hour as global, we do not need additional flags to determine whether the technology meets 1 minutes, or the count satisfies 1 hour. You only need to judge whether it is equal to 59, because the next judgment will be made on the next clock edge, and wouldn’t the next clock edge be exactly 60 (assignment at the last moment, judgment at the next moment, be careful to use non-blocking assignment). So what we have to do is to maintain these count values and automatically clear them when they reach 59/23.

When each second is accumulated, LED1 is flipped, and in the code that accumulates for 59 seconds and is automatically cleared, LED2 is flipped. I won’t expand on the specifics, you can read it while adjusting it in conjunction with the code.

// seconds part
always @ (posedge clk_div or posedge rst)begin
       if(rst)begin
           sec <= 0;
           led_out1 <= 1'b0;
           led_out2 <= 1'b0;
       end
      else begin
        if(sec==59)begin
             sec <= 0 ;
             led_out2 <= ~led_out2;
            end
        else begin
            led_out1 <= ~led_out1;
            sec <= sec + 1 ;
        end
    end
end

We also need to talk about the frequency division part, which is more important.

Variable Meaning
clk_out1 50MHz clock signal obtained after PLL frequency division
cnt FPGA self-increasing count value
clk_div FPGA accumulates signals for 1 second (rising edge)
sec second count value (after 59 Clear)
min Minute count value (cleared after 59)
hour Clock count value (cleared after 23)
parameter TIME_2HZ = 25_000_000;
parameter TIME_1HZ= 50_000_000;

//Frequency division part 50MHz - 1Hz/2Hz
always @ (posedge clk_out1 or posedge rst)begin
       if(rst)begin
           cnt <= 0;
           clk_div <= 0;
        end
       else begin
        if ( cnt < TIME_2HZ - 1 )
           begin
               clk_div <= 0;
               cnt <= cnt + 1;
           end
        else if ( cnt < TIME_1HZ - 1 )
           begin
               clk_div <= 1;
               cnt <= cnt + 1;
           end
        else
           cnt <=0 ;
       end
end

In addition, since the onboard crystal oscillator source is 200MHz, we need PLL frequency division to reduce the crystal oscillator source to 50MHz. Please refer to the previous blog for this part. If your crystal oscillator is already 50MHz, just replace clk_out1 with sys_clk (50MHz).

The above blog address: [Solved] FPGA series Xilinx Artix7 cannot find where SYS_CLK is, how to output a 50MHZ frequency square wave

The logic is like this, the system clock is now 50MHz, counting once is 20ns, we can count the accumulated 50_000_000 and flip once clk_div; and the method used here is slightly different, which is equivalent to a 500ms high level and a 500ms low level. When the two high levels The interval is still 1 second. But if we flip the state of clk_div once every second, then the interval between two rising edges is 2 seconds (1 second high level and 1 second low level) instead of 1 second.

Looking at the collection of images, assuming that the shaded part is 1 second of natural time, then plan 1 is to maintain the low level of 500ms and maintain the high level of 500ms. Option 2 is to maintain the high level for 1_000ms, which is 1 second, and then maintain the low level for 1_000ms. Which of the above two options is closer to our needs? It is obviously the first solution, because it can maintain a valid rising edge clk_div every 1 second, while the solution 2 needs to deliver a valid high level every 2 seconds.

Another thing to note is that a clk_div signal is generated every 1 second, and in the timing processing functions of seconds, minutes, and clocks, the rising edge of clk_div is as a trigger condition. There seems to be no problem, but in fact there is a better way to write it. Theoretically, the system clock (50MHz) should be used as the trigger condition instead of the self-divided clock, and after the system clock is triggered, the rising edge of clk_div should be judged to complete a series of operations. Although there are currently no problems with the project, I will mark it here first to see if there are any other experts who can give me some advice.

After figuring out these main logics, reading the following code should be smoother. If you have any specific questions, you can leave a message in the comment area.

Code Engineering

First of all, I want to make it clear that the current stage of entry into FPGA is very special. All engineering codes and engineering logic may have errors. I hope all students and teachers can point them out. At the same time, I also refer to a large number of high-quality content on the Internet. Thank you here. Based on This reason also decided to share the core logic. Please do not use it for any commercial purposes.

In addition, this project also continues to use hierarchical design. Specifically, what is hierarchical (only a personal understanding), and what benefits does code reuse bring? Please read my other blog, and I will not go into details here. The following code contains three parts. The first part is the implementation of the core clock module. The second part is Top.v, which is the logic finally downloaded to the board. This part calls the clock module and declares the output of some ports. The third part is Testbench. It also calls the clock module and declares the output of the corresponding port. The difference is that sec and min and hour, after all, these are the three we want to look at! ? !

My other blog: FPGA is developed based on Vivado, and the top-level design file Top.v

The complete clock module code, the main function is to output a valid rising edge clk_div every 1 second through the cooperation of clock + count value under the 50MHz crystal oscillator source, and enter the specific logic judgment , including operations such as count value accumulation and count value clearing. You can take a look at the details by combining code + simulation.

`timescale 1ns / 1ns

//digital clock module
module clock1(
    sec,
    min,
    hour,
    rst,
    sys_clk_p,
    sys_clk_n,
    clk_out1,
    led_out1,
    led_out2
    );
       
input rst;
input sys_clk_p; // Differential input clock 200Mhz
input sys_clk_n; // Differential input clock 200Mhz
output clk_out1;
output [7:0] sec,min;
output [7:0] hour;
output led_out1;
output led_out2;
reg [7:0] sec=0;
reg [7:0] min=0;
reg [7:0] hour=0;
reg [32:0] cnt;
reg clk_div;
reg led_out1;
reg led_out2;

//parameter TIME_2HZ = 25; //Used for test simulation
//parameter TIME_1HZ= 50;
parameter TIME_2HZ = 25_000_000;
parameter TIME_1HZ= 50_000_000;


//Frequency division part 50MHz - 1Hz
always @ (posedge clk_out1 or posedge rst)begin
       if(rst)begin
           cnt <= 0;
           clk_div <= 0;
        end
       else begin
        if ( cnt < TIME_2HZ - 1 )
           begin
               clk_div <= 0;
               cnt <= cnt + 1;
           end
        else if ( cnt < TIME_1HZ - 1 )
           begin
               clk_div <= 1;
               cnt <= cnt + 1;
           end
        else
           cnt <=0 ;
       end
end

// seconds part
always @ (posedge clk_div or posedge rst)begin
       if(rst)begin
           sec <= 0;
           led_out1 <= 1'b0;
           led_out2 <= 1'b0;
       end
      else begin
        if(sec==59)begin
             sec <= 0 ;
             led_out2 <= ~led_out2;
            end
        else begin
            led_out1 <= ~led_out1;
            sec <= sec + 1 ;
        end
    end
end

//Minute part
always @ (posedge clk_div or posedge rst)begin
   if(rst)begin
        min <= 0;
   end
   else begin
     if(sec==59)begin
         if (min==59)
            min <= 0;
         else
            min <= min + 1;
     end
   end
end

//clock part
always @ (posedge clk_div or posedge rst)
    begin
       if(rst)begin
           hour <= 0;
       end
       else begin
            if ( sec == 59 & amp; & amp; min ==59 )begin
                if (hour == 23)
                    hour <= 0 ;
                else
                    hour <= hour + 1;
            end
       end
end

endmodule

The complete top-level Top.v file code, regular clock module initialization, PLL frequency division is because my board is 200MHz, and the PLL frequency division is 50MHz (written in the previous blog).

`timescale 1ns / 1ps


module TOP(
    input rst,
    input sys_clk_p,
    input sys_clk_n,
    output clk_out1,
    output led_out1,
    output led_out2
    );

//*************Differential clock 50MHz ********************************** *******
wire sys_clk_p;
wire sys_clk_n;
wire rst;
wire clk_out1;

//***********Digital clock*********************************** *****
wire led_out1;
wire led_out2;


//Clock module initialization
clock1 clock(
    .led_out1(led_out1),
    .led_out2(led_out2),
    .rst(rst),
    .sys_clk_p(sys_clk_p),
    .sys_clk_n(sys_clk_n),
    .clk_out1(clk_out1)
    );

//PLL frequency division code
clk_wiz_0 clk_wiz_0
(
// Clock out ports
.clk_out1(clk_out1), // output clk_out1
// Status and control signals
.reset(rst),
//.locked(locked), // output locked
// Clock in ports
.clk_in1_p(sys_clk_p),
.clk_in1_n(sys_clk_n)); // input clk_in1_n

endmodule

Then there are simulation files, which are also relatively conventional. Note that the three outputs of sec, min, and hour are also declared here to facilitate us to see its waveform. Another point worth noting is that the digital clock counting method here is hexadecimal, which means 09, 0a, 0b, 0c ... This counting method, I haven’t figured out the specific reason yet. clear. Later, when I made this clock into a digital clock and displayed it, I did some additional processing, and finally it was able to be displayed normally as 09, 10, 11, 12... In this form, if you are interested, you can Check out that blog.

`timescale 1ns / 1ns


module clock1_tb();

//******************************************* **********
reg sys_clk_p;
wire sys_clk_n;
reg rst;
wire clk_out1;
//******************************************* **********
wire [7:0] sec;
wire [7:0] min;
wire [7:0] hour;
wire led_out1;
wire led_out2;


//Initialize system clock
initial begin
    sys_clk_p = 1'b0;
    rst <= 1'b0;
    #2000
    rst <= 1'b1;
    #20000
    rst <= 1'b0;
    #2000000
    rst <= 1'b1;
    #20000
    rst <= 1'b0;
end


parameter T = 5; //200MHz clock cycle is 5ns
always #(T/2) sys_clk_p = ~ sys_clk_p;
assign sys_clk_n = ~ sys_clk_p;


//Digital clock initialization
clock1 clock(
    .led_out1(led_out1),
    .led_out2(led_out2),
    .sec(sec),
    .min(min),
    .hour(hour),
    .rst(rst),
    .sys_clk_p (sys_clk_p ),
    .sys_clk_n (sys_clk_n ),
    .clk_out1 (clk_out1 )
    );

//PLL clock frequency division
clk_wiz_0 clk_wiz_0
(
// Clock out ports
.clk_out1(clk_out1), // output clk_out1
// Status and control signals
.reset(rst),
//.locked(locked), // output locked
// Clock in ports
.clk_in1_p(sys_clk_p),
.clk_in1_n(sys_clk_n)); // input clk_in1_n

endmodule

You can burn the board later to see the phenomenon in detail. The pin diagram has been given above, but our pins will not be the same. In addition, because my clock is 200MHz, there will be sys_clk_p, sys_clk_n (the difference between the two), and clk_out1 (the final 50MHz actually does not need to be output). The reset of the board is active at high level, which may be somewhat different from the traditional reset. Please pay attention to this. If you reset with a low level, it is recommended to write it as rst_n. I reset with a high level, so it is rst.

Summary

  • Through the combination of clock + count value, a rising edge with a period of 1 second is generated in the FPGA environment clk_div;
  • Based on this rising edge, the timing logic of seconds, minutes, and clocks is implemented;
  • The final display effect is that LED1 flips every 1 second (completes one flash in 2 seconds), and LED2 flips every 1 minute.

Reference

  • FPGA Verilog Development Practical Guide – Based on Xilinx Artix7, Wildfire (highly recommend this book and their free prostitution materials);
  • FPGA based digital clock (using vivado)
  • Simple clock based on FPGA (including verilog source code)

above.

If you think this article is helpful to you, please give me a like and thank you
If you encounter other problems, please leave a message in the comment area

syntaxbug.com © 2021 All Rights Reserved.