This is the second part of my Hello AFU tutorial. In the last part we setup the base project and wrote a few scripts that will help view signals in ModelSim.
In this part we’ll look at the signals received by the AFU from the PSL and implement the reset handling required by all AFUs.
Resetting the AFU
The first thing the PSL requests of the AFU is to reset to a known good state. This is very easy to implement at this point as we have no internal state! Let’s look at the signals coming in for this.
There are two implemented job control commands documented in the CAPI User’s Manual: START(0x90) and RESET(0x80). Initially and between jobs, the RESET command is sent to the AFU. The expectation on the AFU when given a RESET command is that it will reset its internal state, then raise the
ah_jdone signal for a cycle.
Tip: All signals that begin with ah_ represent signals from the Accelerator to Host, signals starting with ha_ represent from the Host to the Accelerator
The purpose for each signal is documented within the CAPI User’s Manual, for the RESET command only the
ha_jcom are important on the receiving side.
ha_jcompar should also be properly set to set to the odd parity bit.
Before we handle this signal, let’s fix all the floating signals we’re sending back. They are floating because we aren’t explicitly setting these signals high or low, so lets set them all low. I’ll set the
parity_enabled signals low while I’m at it. The signal names are different in my code as I’ve given them more verbose names in the
capi.sv package file used to abstract these out. I’ve added this to my
parity_afu module definition just above the
assign job_out.running = 0, job_out.done = 0, job_out.cack = 0, job_out.error = 0, job_out.yield = 0, timebase_request = 0, parity_enabled = 0;
Additionally, I will need to uncomment the portion of
afu.sv that routes these signals into the AFU.
Also, before testing this in the simulator, I write one more do file
test.do to make it easier to see what I’m working on in simulation. It will prepare simulation, watch the job interface, then run for 10 cycles.
vsim work.top do watch_job_interface.do run 40
With those changes made I’ll verify the signals are now being driven low.
There two main ways to drive signals in SystemVerilog, blocking
= and non-blocking
<=. These can be confusing terms, this page can help clear it up a little bit. Until you’re comfortable with the difference I suggest you use
= only in
assign statements where you are driving a signal to a constant value as we are so far. When you use
<=, the value will stick in a register, preserving it’s value until changed later.
To send our
ah_jdone signal, we need to detect when the
ha_jval is high combined with a
ha_jcom set to RESET, then we’ll know it’s appropriate to raise the
For signals that we want to change during a clock cycle, we can put non-blocking assignments in an
always_ff block. We first need to remove the blocking assignment made with the
assign command as only one driver can be used to set the signal. Next we’ll add an
if statement that will drive the
done signal high only if a valid reset command is given, we also need to set it low in all other conditions so we’ll use an
else statement to ensure we get that behavior.
always_ff @(posedge clock) begin if(job_in.valid & job_in.command == RESET) begin job_out.done <= 1; end else begin job_out.done <= 0; end end
I’ve been advised that there is one thing wrong with this design, the done signal should be sent on the next clock cycle. We need something to help us delay the signal.
Making a shift register
There are a few ways to do this, I’ve elected to use a shift register to fulfill this need.
This shift register will pass its input to its output, delaying changes by a single clock cycle. It’s not the most useful shift register but it will do for this purpose.
module shift_register ( input logic clock, input logic in, output logic out); always_ff @ (posedge clock) begin out <= in; end endmodule
To use this module in our
parity_afu module, we’ll need to create an instance of our
shift_register module and a variable to reference its input. In the instance we create we’ll reference the inputs and outputs, then change our job logic to use the new
jdone variable instead of the direct output.
logic jdone; shift_register jdone_shift( .clock(clock), .in(jdone), .out(job_out.done));
Once this is all said and done, we should get the output shifted back a cycle as we desired. Since the shift register is setup with our internal
jdone as input and the
job_out.done as output, this will affect all assignments to
See these changes committed here.
This should be sufficient to handle the reset command for now. The next set of signals our AFU will receive will be requests for the AFU descriptor over the MMIO interface, I’ll walk through implementing this in my next post.