0
\$\begingroup\$

I have the following two signals A and B

      __________                                           __________
_____|          |_________________________________________|          |_____
 __                   __    __    __    __    __    __                  __
|  |_________________|  |__|  |__|  |__|  |__|  |__|  |________________|

Note that:

  • There is no common clock between them available
  • Signal B always changes when A is low and A always changes when B is low

I would like to create a 6-bit counter that increments its value every negative edge of signal A and then outputs its value bitwise in between, exactly at the positive edges of signal B.

The problem is I have to share a state between these two, for example:

wire bit_output;
reg [5:0] counter;
reg [5:0] current_value;
always @(posedge signalA) begin
  counter <= counter + 1;
  current_value <= counter;
end

always @(posedge signalB) begin
  current_value <= { current_value[4:0], 1'b0 };
end

assign bit_value = current_value[5];

Now this is unfortunately not a valid Verilog code, for example in Xilinx ISE I get:

Signal current_value[5] in unit test is connected to following multiple drivers:

I tried placing them into one always statement but this is not possible either:

Assignment under multiple single edges is not supported for synthesis

If I had a common clock I could use a conventional state machine but in this case I only have these two independent signals.

No matter with which solution I come up, I always need to share data somehow.

I do not want to run both paths independently; for example having a counter for SignalB and if it is zero, initialize with data from A because the initial condition would be undefined. I would like to ensure that the data that is spit out on the positive edges of signal B are always properly initialized at the falling edge of signal A even if, for example, only 3 positive signal B edges occur.

What is the easiest way to implement this?

(this is just a simplified example to keep things compact; the actual task is more complicated)

\$\endgroup\$
  • 1
    \$\begingroup\$ If you had to implement this with actual logic components, how would you do it? If you're not sure of that, then this is a logic design question, not a Verilog question. \$\endgroup\$ – The Photon Mar 30 '18 at 15:34
1
\$\begingroup\$

To do this in an FPGA, the usual way would be to introduce a new clock, running much faster than A or B. Clock your logic from this new clock and use A and B only as control signals, not as clocks.

The new clock must be fast enough to be guaranteed to have at least one edge for each change of value of the A and B inputs.

module weird_counter(A, B, CLK, Q);
input A, B;
input CLK;
output Q;

reg [2:0] Ad;  
reg [2:0] Bd; /* Delayed copies of A and B */
reg [5:0] ctr;
reg [5:0] obuf;

assign Q = obuf[5];

always @(posedge CLK) begin
    Ad <= {Ad[1:0], A};
    Bd <= {Bd[1:0], B};

always @(posedge CLK) begin
    if(Ad[1] & ~Ad[2]) begin /* We are seeing a rising edge on A */
         /* Do something with ctr and obuf*/
    end
    if(Bd[1] & ~Bd[2]) begin /*Rising edge on B */
         /* Do something else with ctr and obuf*/
    end
end

endmodule;

Edit: Added some stages to Ad and Bd at Tom Carpenter's suggestion to demonstrate syncronizing a signal across clock boundaries. This reduces the risk of a logic fault due to violating set-up and hold times.

\$\endgroup\$
  • 2
    \$\begingroup\$ I'd also add a couple or registers to synchronise the signals to the clock given they have no common clock. \$\endgroup\$ – Tom Carpenter Mar 30 '18 at 15:56
0
\$\begingroup\$

If I use this specification:
Signal B always changes when A is low and A always changes when B is low, I would like to create a 6-bit counter that increments its value every negative edge of signal A and then outputs its value bitwise in between, exactly at the positive edges of signal B.

I get this code:

always @(negedge A)
   if (B==0)
      counter <= counter + 1;

always @(posedge B)
   if (A==0)
     bit_out <= bit_out + 1;

assign serial_out = counter[bit_out];

I you can guarantee the transition do no happen when the signals are high you can omit the two "if (A/B==0)" sections.
As to in between, exactly at the positive edges of signal. You must control that by correctly making the A and B signals.


You added:

if, for example, only 3 positive signal B edges occur.? In this case, the next cycle should still have a fresh start. bit_out has to be set to zero by the first always @ statement

You might still do that by using A as a-synchronous reset:

always @(posedge B or posedge A)
   if (A)
      bit_out <= 0; // reset counter when A is high 
   else
      bit_out <= bit_out + 1;

The rising edge of B must not coincide with the falling edge of A.

But there comes a time when you can't do these things with purely synchronous logic. For example I2C is very, very difficult to do in pure Verilog logic and most of the time the interface runs using a clock at a much higher frequency.

\$\endgroup\$
  • \$\begingroup\$ But what about the requirement even if, for example, only 3 positive signal B edges occur.? In this case, the next cycle should still have a fresh start. bit_out has to be set to zero by the first always @ statement ... \$\endgroup\$ – divB Mar 30 '18 at 9:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.