I have implemented a serial-in parallel-out register implemented in SystemVerilog:
module sipo_reg(
input wire in_data,
output wire [7:0] out_data,
output wire rdy,
input wire rst,
input wire clk
);
reg [7:0] buff;
reg [2:0] i;
reg was_enabled;
always @(posedge clk, posedge rst) begin
if (rst) begin
buff <= 0;
i <= 7;
was_enabled <= 0;
end else begin
was_enabled <= 1;
buff[i] <= in_data;
i <= i == 3'd0 ? 3'd7 : (i - 3'd1);
end
end
assign rdy = (i == 3'd7) & was_enabled;
assign out_data = {8{rdy}} & buff;
endmodule
I'm wondering if this is not a very good design because it might happen that rdy
goes high before out_data
has settled, leading to an incorrect read.
For instance, in the following code:
always @(posedge clk) begin
if (rdy)
my_data <= out_data;
end
It could happen that rdy
reaches the positive edge before out_data
has settled.
However, given that rdy
changes some small delay after (posedge clk)
, one would say that rdy
is asynchronous with respect to clk
and can't just be read on the clock edge, since it could not respect the flip-flop setup-and-hold times. It would need to be synchronized to some clock signal, by passing it through two back-to-back flip flops (as explained here). Is this correct?:
cell_sync cs(clk, rst, rdy, rdy_sync); // rdy_sync synchronized with clk
always @(posedge clk) begin
if (rdy_sync)
my_data <= out_data;
end
However, in the code above, the problem no longer exists since rdy_sync
will go high almost two clocks later than rdy
does. As long as the clock period is slow enough to assure that all combinational logic in the design will have time to settle, out_data
will necessarily have settled before it is read, am I right?
I have tried this on my simulation, and it works, yet I wonder if my reasoning is correct specially when implementing this design in a physical FPGA.
Finally, the most problematic scenario I can think about is:
always @(posedge rdy)
my_ff <= out_data;
This already fails in my simulations, so I have replace the last two continuous assignments by:
wire _rdy;
assign _rdy = (i == 3'd7) & was_enabled;
assign rdy = ~(~(~(~_rdy)));
assign out_data = {8{_rdy}} & buff;
The above works in my simulation, but I'm not very sure about the physical FPGA:
Having that out_data
only needs 8 and gates in parallel, and rdy
needs 4 not gates in series, assuming that the delay of the and and or gates are the same (by and and or gates I mean whatever logic the fpga actually uses to implement then), out_data
should settle way before rdy
does. Still, this still looks a bad design to me, because seems unsafe. Would I be right trying to avoid this solution? Can I be sure the synthesizer will not optimize out those not gates?
In addition, it seems bad to me to do @(posedge x)
, where x
is anything other than a clock or a reset signal. Is this true?