Verilog: how to synchronously assign wire out with register

fpgaverilog

This is the output of ISim simulation:

ISim

I want to decrease tx_data_ctr by 1 when flags_from_clk_div turns to 4'b0000 so sda_flag_from_transmit_byte take initial bit from tx_data[7:0]. However, I could not find a way to do it. What I actually ask is if there is a way to set flag or do something else synchronously with the certain change of a register?

It is like @(reg_x == 4'b0001) – do_stuff but of course I know there is no such command.

Also, is one able to do stuff with posedge like when register change immediately execute something?

I use configurations like:

always @(posedge start) begin
  // stuff stuff
end

but can we apply something like this to a register has more than one byte? I hope I could explain.

edit: I added codes below:

scl_counter.v:

module scl_counter(
    input rst,
    input start,
    output reg [3:0] flags,
    input clk
);

parameter clk_divider = 0;

reg [15:0] scl_counter;

always @(posedge start) begin
    flags = 4'b0;
    scl_counter = 16'b0;
end  

always @(posedge clk) begin
    if (rst) begin
        flags = 4'b0;
        scl_counter = 16'b0;
    end
    else if (!rst) begin
        if (clk_divider != 1) begin
            if (scl_counter != (clk_divider*2)) begin
                scl_counter = scl_counter + 1;
                if (scl_counter == (clk_divider/2)) begin
                    flags[0] = 1'b1;
                end
                else if (scl_counter == (clk_divider*3/2)) begin
                    flags[1] = 1'b1;
                end
            end
            else if (scl_counter == (clk_divider*2)) begin
                scl_counter = 16'b0;
                flags = 4'b0;
            end
        end
        else if (clk_divider == 1) begin

        end
    end
end

endmodule

transmit_byte.v:

`include "scl_counter.v"
module transmit_byte(
        output reg [0:0] sda_flag_from_transmit_byte,
        input [7:0] tx_data,
        output reg [7:0] rx_data,
        input clk,
        output reg [0:0] scl_flag_from_transmit_byte,
        input start_scl_div,
        output reg [0:0] reset_to_clk_div,
        input [3:0] flags_from_clk_div,
        output reg [2:0] tx_data_ctr

);

reg [2:0] rx_data_ctr;
reg [0:0] ready_to_start_flag;
reg [0:0] resetter_flag_clk_div;

always @(posedge start_scl_div) begin
    scl_flag_from_transmit_byte = 1'b0;
    ready_to_start_flag = 1'b0;
    resetter_flag_clk_div = 1'b0;
    tx_data_ctr = 3'b111;
    rx_data_ctr = 3'b0;
    ready_to_start_flag = #1 1'b1;

end

always @(negedge clk) 

begin: RESETTER_TO_CLK_DIV

    if (ready_to_start_flag) begin
        if (start_scl_div) begin
            if (resetter_flag_clk_div == 1'b1) begin
                reset_to_clk_div = 1'b0;
            end
            else if (resetter_flag_clk_div == 1'b0) begin
                reset_to_clk_div = 1'b1;
                resetter_flag_clk_div = 1'b1;
            end
        end
    end

end



always @(posedge clk) begin
    if (start_scl_div) begin
        if (ready_to_start_flag) begin
            if (flags_from_clk_div == 4'b0) begin
                sda_flag_from_transmit_byte = tx_data[tx_data_ctr];
            end
            else if (flags_from_clk_div == 4'b0001) begin
                scl_flag_from_transmit_byte = 1'b1;
            end
            else if (flags_from_clk_div != 4'b0001) begin
                scl_flag_from_transmit_byte = 1'b0;
            end
        end
    end
end

always @(negedge start_scl_div) begin
    ready_to_start_flag = 1'b0;
    resetter_flag_clk_div = 1'b0;
end
//b01100100
scl_counter #(16'b00001000) c_1(
    .rst    (reset_to_clk_div),
    .start  (start_scl_div),
    .flags  (flags_from_clk_div),
    .clk    (clk)
);

endmodule

timer_A.v:

module timer_A(
    input clk,   // which clock?
    input rst,   // sets to 0 or up counter
    //output [7:0] flags_timer_A,   // sets flag when counts to the value
    input mode,   // if mode 0, counts up to A only flags A, if 1 counts to
                        // A and B, C, D ... flags if they are not 0.
    input [15:0] count_to_A,  // counts to first value
    input [15:0] count_to_B,  // counts to second value
    input count_to_C,
    input count_to_D,
    input count_to_E,
    input count_to_F,
    input count_to_G,
    input count_to_H,
    output reg [7:0] flags_timer_A
);

reg [15:0] timer_A_Reg;

//reg [7:0] flags_timer_A;

/*
timer_A_flag_A  = flags_timer_A[0]

timer_A_flag_B  = flags_timer_A[1]

timer_A_flag_C  = flags_timer_A[2]

timer_A_flag_D  = flags_timer_A[3] ...
*/


always @(posedge rst) begin
    flags_timer_A = 8'b0;
    timer_A_Reg = 16'b0;
end

always @(posedge clk) begin
    if (rst) begin
        flags_timer_A = 8'b0;
        timer_A_Reg = 16'b0;
    end
    else if (!rst) begin
        if (mode == 1'b0) begin
            if (timer_A_Reg != count_to_A) begin
                timer_A_Reg <= timer_A_Reg + 1;
            end
            else begin
                flags_timer_A[0] <= 1'b1;
            end
        end
        else begin
            if (timer_A_Reg != count_to_A) begin
                timer_A_Reg = timer_A_Reg + 1;
                if (timer_A_Reg == count_to_B) begin
                    flags_timer_A[1] = 1'b1;
                end
                else if (timer_A_Reg == count_to_C) begin
                    flags_timer_A[2] = 1'b1;
                end
                else if (timer_A_Reg == count_to_D) begin
                    flags_timer_A[3] = 1'b1;
                end
                else if (timer_A_Reg == count_to_E) begin
                    flags_timer_A[4] = 1'b1;
                end
                else if (timer_A_Reg == count_to_F) begin
                    flags_timer_A[5] = 1'b1;
                end
                else if (timer_A_Reg == count_to_G) begin
                    flags_timer_A[6] = 1'b1;
                end
                else if (timer_A_Reg == count_to_H) begin
                    flags_timer_A[7] = 1'b1;
                end
            end
            else begin
                flags_timer_A[0] <= 1'b1;
            end
        end
    end
end


endmodule

start_i2c.v:

    `include "timer_A.v"
    module start_i2c(
            input start,
            input [7:0] flags_timer_A,
            input clk,
            output reg [0:0] rst_to_tmr,
            output reg [0:0] start_done,
            output reg [0:0] scl_flag_from_start_i2c,
            output reg [0:0] sda_flag_from_start_i2c
    );

    reg [0:0] resetter_flag;
    reg [0:0] mode_to_tmr;
    /*
    timer_A_flag_A  = flags[0]

    timer_A_flag_B  = flags[1]

    timer_A_flag_C  = flags[2]

    timer_A_flag_D  = flags[3] ...
    */

    always @(posedge start) begin
        resetter_flag <= 1'b0;
        mode_to_tmr <= 1'b1;
        start_done <= 1'b0;
        scl_flag_from_start_i2c <= 1'b1;
        sda_flag_from_start_i2c <= 1'b1;
    end

    parameter min_SDA_on_time = 0;  
    parameter min_SDA_SCL_fall_delay = 0;

    always @(negedge clk)
    begin: RESETTER    // this resets up when start is on immediately
        if (start && !resetter_flag) begin
            rst_to_tmr = 1'b1;
            resetter_flag = 1'b1;
        end
        else if (start && resetter_flag) begin
            rst_to_tmr <= 1'b0;
        end
    end

    always @(posedge clk) begin
        if (start) begin
            if (flags_timer_A[1]) begin
                sda_flag_from_start_i2c <= 1'b0;
            end
            if (flags_timer_A[0]) begin
                scl_flag_from_start_i2c <= 1'b0;
                start_done <= 1'b1;
            end
        end
        else begin

        end
    end

    always @(negedge start) begin
        resetter_flag = 1'b0;
        //start_done <= 1'b0;
    end

    timer_A  start_timer(
        .clk             (clk),   // which clock?
        .rst             (rst_to_tmr),   // sets to 0 or up counter
        .mode     (mode_to_tmr),   // if mode 0, counts up to A only flags A, if 1 counts to
                                            // A and B, C, D ... flags if they are not 0.
        .count_to_A      (min_SDA_on_time + min_SDA_SCL_fall_delay),  // counts to first value
        .count_to_B      (min_SDA_on_time),  // counts to second value
        .count_to_C      (16'b0),
        .count_to_D      (16'b0),
        .count_to_E      (16'b0),
        .count_to_F      (16'b0),
        .count_to_G      (16'b0),
        .count_to_H      (16'b0),
        .flags_timer_A   (flags_timer_A)  // sets flag when counts to the value
    );

    endmodule

**start_i2c_tb.v:**

`include "start_i2c.v"
`include "transmit_byte.v"

module start_i2c_tb(
    );

//defparam start_test.min_SDA_on_time = 16'b11001000;

//defparam start_test.min_SDA_SCL_fall_delay = 16'b01100100; 

reg [0:0] start_start_i2c;

reg [0:0] start_scl_div;

reg [7:0] tx_data;

wire [7:0] flags_timer_A;

reg [0:0] clk;

wire sda_flag_from_transmit_byte;

wire scl_flag_from_transmit_byte;

wire scl_flag_from_start_i2c;

wire sda_flag_from_start_i2c;

wire [3:0] flags_from_clk_div;

wire [2:0] tx_data_ctr;

initial begin

    clk = 1'b0;
    start_scl_div = 1'b0;
    start_start_i2c = 1'b0;
    #5 start_start_i2c = 1'b1;
    tx_data = 8'b10101010;
    #40000 $finish;

end

always begin
    #1 clk = ~ clk;
end

always @(clk) begin
    if (start_done && start_start_i2c) begin
        start_start_i2c = 1'b0;
    end
    else if (start_done && !start_start_i2c) begin
        start_start_i2c = 1'b0;
        start_scl_div = 1'b1;
    end
end

start_i2c #(16'b11001000, 16'b01100100) start_test(
        .start          (start_start_i2c),
        .flags_timer_A  (flags_timer_A),
        .clk            (clk),
        .rst_to_tmr     (rst_to_tmr),
        .start_done     (start_done),
        .scl_flag_from_start_i2c            (scl_flag_from_start_i2c),
        .sda_flag_from_start_i2c            (sda_flag_from_start_i2c)
);

transmit_byte start_transmit(
        .sda_flag_from_transmit_byte       (sda_flag_from_transmit_byte),
        .tx_data   (tx_data),
        .rx_data   (rx_data),
        .clk       (clk),
        .scl_flag_from_transmit_byte       (scl_flag_from_transmit_byte),
        .start_scl_div   (start_scl_div),
        .reset_to_clk_div  (reset_to_clk_div),
        .flags_from_clk_div  (flags_from_clk_div),
        .tx_data_ctr   (tx_data_ctr)

);

endmodule

EDIT: Based on Dave Tweed's answer, I added the following code block and it works now. Is it right way to do it?

always @(posedge clk) begin
    if (flags_from_clk_div == 4'b0) begin
        if (tx_check) begin
            tx_data_ctr = tx_data_ctr + 1;
            tx_check = 1'b0;
        end
    end
    if (flags_from_clk_div == 4'b0011) begin
        tx_check = 1'b1;
    end
end

Best Answer

You can always create a new signal that represents the condition you want to test, such as:

wire x_is_one = (x == 1);

Then you can look for events on that signal.

However, basing a design on a lot of asynchronous events is a risky approach, very prone to error. Try to find a synchronous solution — one in which all of the logic is clocked by a common clock. This is especially true when working with FPGAs, for which both the hardware architecture and the software tools are strongly oriented toward synchronous designs.