Distributed: March 5 - Due Friday, March 12 (beginning of class)
1. Let’s return to the problem from HW #4 (copied below) and replace the registers with real registers that have setup and hold times and propagation delays. Use the following values for all the registers: Setup time = 2ns, Hold time = 1ns, Propagation delay = 3ns.
a) If you draw the data-flow graph directly from the code below, what is the minimum clock period that can be used? Use the register parameters given above.
b) How do you change the max-ratio theorem so that it is still valid? Explain why it still holds with your modifications.
c) What is the minimum theoretical clock period for this computation now that we have “real” registers?
d) Does the fact that we have real registers change the pipelining/retiming used to produce the fastest circuit? Why can you still use your result from HW4?
e) What is the performance now for the optimally pipelined/retimed circuit?
Now assume that there can be arbitrary clock skew of up to 1ns. between any two registers.
f) Does the original circuit (part a) still work? Why/why not?
g) How fast can you now clock the optimally pipelined/retimed circuit? Show how you get your answer.
*h) Can you use retiming to increase the amount of clock skew a circuit can tolerate? Explain.
Below is a computation that is performed repeatedly on an input stream to produce an output stream. Multiplies take 10ns. and adds and take 5ns.
for (i=0;i<N;i++) {
t1 = x[i] * t6;
t6 = x[i] + t5;
t2 = t1 * t6;
t5 = t1 + t4;
t4 = t2 + t3;
t3 = t2 * t5;
y[i] = t6;
}
2. A very nice way to send data from one circuit to another that is running on a different clock is to place a FIFO between them. The sender writes data into the FIFO using its clock, and the receiver reads data from the FIFO using its clock. This should work fine because writing data into the FIFO clocks one counter (and the memory) while reading data from the FIFO clocks the other counter and the memory output is used combinationally.
a) Given below is the Verilog for a synchronous FIFO. Modify this FIFO to make it into an asynchronous FIFO, i.e. a FIFO that has two clocks, one for the input port and one for the output port. (Ignore problems that arise because of the two clocks – it will “mostly” work.)
b) The asynchronous FIFO that you have designed does not work because it uses two different clocks. Describe precisely where these problems happen.
c) Now fix the asynchronous FIFO you designed so that it does work. You will need to replace the normal counters used in the synchronous FIFO with gray-code counters. (The Verilog for a gray-code counter is given below as well.) Explain why you need to use gray-code counters? (You do not need to simulate the resulting designs – simulation will not tell you if it works or not anyway!)
module
sync_fifo(/*AUTOARG*/
// Outputs
full, data_out, empty,
// Inputs
clk, reset, data_in, dataValid, takingData
);
parameter ADDR_BITS = 3;
parameter
MEM_DEPTH = 8;
parameter
WIDTH = 32;
input
clk;
input
reset;
input [WIDTH-1 :0] data_in;
input dataValid;
output full;
output [WIDTH-1 :0] data_out;
output empty;
input takingData;
//
FIFO head and tail pointers
reg [ADDR_BITS-1:0] wr_ptr;
wire [ADDR_BITS-1:0] wr_ptr_1;
reg [ADDR_BITS-1:0] rd_ptr;
// FIFO memory
reg [WIDTH-1:0] mem [0:MEM_DEPTH-1];
//
Write pointer generation.
// It
increments when there is a dataValid.
// It
resets to zero if there is a reset
// Share the + 1 with the full computation
// (Synthesis should find this common
expression though)
assign wr_ptr_1 = wr_ptr + 1;
always @ (posedge clk) begin
if (reset)
wr_ptr <= 0;
else if (dataValid & !full)
wr_ptr <= wr_ptr_1;
end
// Read pointer generation.
// It increments when there is a takingData.
// It resets to zero if there is a reset.
always @ (posedge clk) begin
if (reset)
rd_ptr <= 0;
else if (takingData & !empty)
rd_ptr <= rd_ptr + 1;
end
assign full = (wr_ptr_1 == rd_ptr);
assign empty = (rd_ptr == wr_ptr);
// Read port
assign data_out = mem[rd_ptr];
// Write port
always @ (posedge clk) begin
if (dataValid && !full)
mem[wr_ptr] <= data_in;
end
endmodule
module
gray_count (/*AUTOARG*/
// Outputs
gray_cnt, gray_nxt,
// Inputs
clk, reset, cnt_ena
);
parameter CNT_WIDTH = 4;
input
clk; // Clock input (Rising
edge sampled)
input
reset; // System Reset -
Asynchronous, Active Low
input
cnt_ena; // Count enable
output [CNT_WIDTH-1:0] gray_cnt;// Count
output
output [CNT_WIDTH-1:0] gray_nxt;
reg [CNT_WIDTH-1:0] bin_cnt;// Binary counter -
hidden state
/*AUTOREG*/
// Beginning of automatic regs (for this
module's undeclared outputs)
reg [CNT_WIDTH-1:0] gray_cnt;
// End
of automatics
// Convert binary count to gray code
assign gray_nxt
= bin_cnt ^ {1'b0, bin_cnt[CNT_WIDTH-1:1]};
always @(posedge clk) begin
if (reset) begin
bin_cnt
<= 1;
// gray_cnt follows bin_cnt by a cycle
gray_cnt <= 0;
end else begin
if (cnt_ena) begin
bin_cnt
<= bin_cnt + 1;
gray_cnt <= gray_nxt;
end
end
end
endmodule