Search code examples
verilogfpgaxilinxvivadozynq

Led Counter Program (0 - 15) with different frequencies


I want to make a counter with the 4 LEDs on a Zybo board that counts from 0 to 15. Also I want the 4 buttons of the board to correspond to a different frequency for the changes of the LEDs (0.5Hz, 1Hz, 2Hz, 4Hz). I have already achieved the simple counter with a fixed frequency but not the second part with the button frequency changes.

In the Block Design, I have a Zynq Processing System, an AXI GPIO which reads the button data and a custom IP that functions as the driver for the LEDs, the clock divider and the frequency changer.

Custom IP

The clock divider module code.

module Clock_Divider(
input clk,  
input rst,
input reg0,
output reg clk_out    
);

reg [31:0] count;    
reg constantNumber;   

always @ (reg0)
begin
    if(reg0 == 0000)
        constantNumber = 50000000;
    else if(reg0 == 0001)
        constantNumber = 100000000;
    else if(reg0 == 0010)
        constantNumber = 50000000;
    else if(reg0 == 0100)
        constantNumber = 25000000;
    else if(reg0 == 1000)
        constantNumber = 12500000;
    else
        constantNumber = 50000000;
end

always @ (posedge(clk), posedge(rst))
begin        
    if (rst == 1'b1)
        begin
        count <= 32'b0;         
        end
    else if (count == constantNumber - 1)
        begin
        count <= 32'b0;
        end
    else
        begin
        count <= count + 1;
        end
end

always @ (posedge(clk), posedge(rst))
begin
    if (rst == 1'b1)
        clk_out <= 1'b0;
    else if (count == constantNumber - 1)
        clk_out <= ~clk_out;
    else
        clk_out <= clk_out;
end   
endmodule

The register constantNumber takes the corresponding value in order to change the clock frequency.

The rest of the custom IP logic.

Clock_Divider UIP (.clk(S_AXI_ACLK), .rst(), .reg0(slv_reg0), .clk_out(clk_out));

reg [3:0] counter = 0;

always @(posedge clk_out) 
begin
    if(counter < PWM_COUNTER_MAX-1)
        begin            
        counter <= counter + 1;
        end
    else            
        counter <= 0;
end

assign PWM0 = counter[0];
assign PWM1 = counter[1];
assign PWM2 = counter[2];
assign PWM3 = counter[3];

The button data are sent to the first register of the custom IP (slv_reg0) which in turn sends them to reg0 in the Clock_Divider module.

Main C program

#include <stdio.h>
#include "platform.h"
#include <xgpio.h>
#include "xparameters.h"
#include "sleep.h"
#include "xil_io.h"

//#define MY_PWM XPAR_MY_PWM_CORE_0_S00_AXI_BASEADDR //Because of a bug in Vivado 2015.3 and 2015.4, this value is not correct.
#define MY_PWM 0x43C00000 //This value is found in the Address editor tab in Vivado (next to Diagram tab)

int main(){
    XGpio input;
    int button_data = 0;

    XGpio_Initialize(&input, XPAR_AXI_GPIO_0_DEVICE_ID);    //initialize input XGpio variable
    XGpio_SetDataDirection(&input, 1, 0xF);                 //set first channel tristate buffer to input

    init_platform();

    while(1){

        button_data = XGpio_DiscreteRead(&input, 1);    //get button data

        if(button_data == 0b0000){
            Xil_Out32(MY_PWM, button_data);
        }

        else if(button_data == 0b0001){
            xil_printf("button 0 pressed\n\r");
            Xil_Out32(MY_PWM, button_data);
        }

        else if(button_data == 0b0010){
            xil_printf("button 1 pressed\n\r");
            Xil_Out32((MY_PWM), button_data);
        }

        else if(button_data == 0b0100){
            xil_printf("button 2 pressed\n\r");
            Xil_Out32((MY_PWM), button_data);
        }

        else if(button_data == 0b1000){
            xil_printf("button 3 pressed\n\r");
            Xil_Out32((MY_PWM), button_data);
        }

        else{
            xil_printf("multiple buttons pressed\n\r");
            Xil_Out32(MY_PWM, 0b0000);
        }
    }

    cleanup_platform();
    return 0;
}

I can confirm that the button data are correctly read by the AXI GPIO because when they are pressed the correct lines are printed in the terminal. But when I press the buttons, the frequency does not change. Also it runs with a very slow frequency, much slower than the 1Hz that should be the default even if the button data did not get sent to the custom IP.

The problem has to lie somewhere either with the reg0 case logic in the custom IP or the sending of the button data from the cpu to the register of the custom IP.


Solution

  • I made the changes that Greg proposed plus some of my own and I got it working at last.

    In the clock divider file I made the following changes.

    input [3:0] reg0,
    
    reg [31:0] constantNumber;   
    
    always @ (reg0)
    begin
        if(reg0 == 4'b0000)
            constantNumber = 50000000;
        else if(reg0 == 4'b0001)
            constantNumber = 100000000;
        else if(reg0 == 4'b0010)
            constantNumber = 50000000;
        else if(reg0 == 4'b0100)
            constantNumber = 25000000;
        else if(reg0 == 4'b1000)
            constantNumber = 12500000;
        else
            constantNumber = 50000000;
    end
    

    I made consantNumber 32 bits in order to be sure there are no overflows.

    In the custom IP logic I changed the parameter for the reg0 in order to send the correct (last) bits of the slv_reg0 to it.

    Clock_Divider UIP (.clk(S_AXI_ACLK), .rst(), .reg0(slv_reg0[3:0]), .clk_out(clk_out));
    

    And last but not least the changes in the c program.

    if(button_data == 0b0001){
            xil_printf("button 0 pressed\n\r");
            Xil_Out32(MY_PWM, 0b0001);
        }
    
        else if(button_data == 0b0010){
            xil_printf("button 1 pressed\n\r");
            Xil_Out32((MY_PWM), 0b0010);
        }
    
        else if(button_data == 0b0100){
            xil_printf("button 2 pressed\n\r");
            Xil_Out32((MY_PWM), 0b0100);
        }
    
        else if(button_data == 0b1000){
            xil_printf("button 3 pressed\n\r");
            Xil_Out32((MY_PWM), 0b1000);
        }
        else if(button_data > 0b0000){
            xil_printf("button 3 pressed\n\r");
            Xil_Out32((MY_PWM), 0b0000);
        }
    

    I got rid of the:

    if(button_data == 0b0000){
            Xil_Out32(MY_PWM, button_data);
        }
    

    because after a few clock ticks no button is pressed and it would send a 0000 signal to the slv_reg0.