1/16
Looks like no tags are added yet.
Name | Mastery | Learn | Test | Matching | Spaced | Call with Kai | Chat |
|---|
No analytics yet
Send a link to your students to track their progress
Explain the line: if((Fifo_Slots & (Fifo_Slots - 1)) != 0)
A power of two has exactly one bit set, so ANDing it with itself minus one gives zero. Any other value gives nonzero, and the initial block prints an error and $finishes at elaboration. It guards the pointer wrap math that assumes power-of-two depth.
Explain the Tx_Full assign: (Write_Pointer[Storage_Log-1:0] == Read_Pointer[Storage_Log-1:0]) && (Write_Pointer[Storage_Log] != Read_Pointer[Storage_Log])
Full detection in the extra-MSB scheme. Lower bits equal means the pointers reference the same slot; MSBs differing means the write pointer has lapped the read pointer exactly once, so the FIFO holds Fifo_Slots entries.
Why is Occupancy computed as Write_Pointer - Read_Pointer?
With the extra MSB the unsigned difference is the exact number of stored bytes, 0 through 16, and wraparound subtraction handles pointer wrap for free. The port is [$clog2(Fifo_Slots):0], 5 bits, precisely because the value 16 needs a fifth bit.
Why does every clocked block use non-blocking assignments?
Non-blocking updates all registers simultaneously from their pre-edge values, matching real flip-flop behavior and making the result independent of statement order. Blocking assignments in sequential logic would create order-dependent, race-prone simulation that can mismatch synthesis.
Explain: Rx_Shift_In <= {Rx_Stable_In, Rx_Shift_In[7:1]}
Concatenation implementing a right shift: bits 7:1 slide down one place and the newly sampled line value becomes bit 7. It is the serial-in-MSB shift register that reverses the wire order into correct LSB-first byte order.
Explain: Read_Data <= {24'd0, Rx_Byte}
Zero-extends the 8-bit FIFO head into the 32-bit AXI read data word, so the byte occupies bits 7:0 and the upper 24 bits read as zero.
Two assignments to Tx_Push_Enable can execute in the same clocked block. Which wins and why is that used?
The last one. With non-blocking assigns to the same register in one block, the final assignment takes effect. Setting it to 0 first and conditionally to 1 later is the default-assignment idiom that produces a clean one-cycle pulse without a separate clear path.
How is Status_Register built and what is in each field?
It is a wire driven by continuous assigns: bit 0 Tx_Busy, bit 1 Rx_Ready, bit 2 Frame_Error, bit 3 reserved zero, bit 4 Over_Run_Error, bit 5 Tx_Full, bits 10:6 TX occupancy, bits 15:11 RX occupancy, bits 31:16 zero. Being combinational, a read always reflects live state.
Why is assign Data_Out = Fifo_Memory_Hold[Read_Pointer[Storage_Log-1:0]] outside any always block?
A continuous assign of a memory word indexed by the read pointer synthesizes to a combinational read mux. That makes the FIFO first-word fall-through: the head byte is always present on the output, which the same-cycle AXI read-and-pop depends on.
What does `timescale 1ns/1ps mean in the testbenches?
Time unit 1 ns, precision 1 ps. A delay like #10 means 10 ns, and the simulator resolves timing to the picosecond. It is why the always #10 clock gives a 20 ns period, 50 MHz.
In the testbenches, why do stimulus tasks do @(posedge Clk_Tb); #1; before driving or sampling signals?
Driving exactly on the clock edge races the DUT: simulator event ordering decides whether the DUT sees the old or new value. The #1 moves testbench activity just past the edge so the DUT samples stable values on the next edge. I hit this for real: dropping Pop_Enable on the edge made the DUT intermittently miss pops and the output froze.
Baud_Count and Sample_Count are declared as integer, which is 32 bits. What does synthesis do with that?
Vivado propagates the bound from the comparison against Baud_Division - 1 and the reset value, then prunes all unreachable upper bits. The routed design's LUT and FF counts show the counters were trimmed to the width the terminal count needs, not 32 bits.
Explain the initial check on Sample_Division in the receiver.
Sample_Division = Clk_Frequency / (Baud_Rate * Over_Sample) is integer division. If the clock is too slow relative to baud times oversample the divider truncates toward zero and sampling would be nonsense, so an initial block errors out at elaboration when it is below 1.
In the TX Data state, what does Tx <= Tx_Shift[Bit_Index] do?
Indexed bit-select of the shift register driven onto the serial line, walking Bit_Index from 0 to 7. Starting at bit 0 sends the LSB first, matching UART ordering, with each bit held for one full baud tick.
What does the default arm of the write-address case do, and what would you improve?
It only sets Bresp to 2'b00, so a write to an undecoded address completes with an OKAY response and no side effects. The textbook improvement is returning DECERR, 2'b10, for unmapped addresses on both Bresp and Rresp.
In the loopback testbench, explain .Uart_Txd(Uart_Loop), .Uart_Rxd(Uart_Loop)
One wire is connected to both the serial output and serial input of the DUT, so the transmitter physically drives the receiver with no testbench modeling in between. Every byte crosses a real 8N1 frame on that wire before coming back through the AXI read path.
Describe the watchdog task in your testbenches.
Timeout_Watch takes a limit in ns, delays that long with #(Limit_Ns), then sets an error flag, prints the summary, and $finishes. It is launched from its own initial block so it runs concurrently with the stimulus; whichever initial finishes first ends the sim. Setting the error flag first matters, otherwise a hang would print ALL TESTS PASSED with zeroed counters.