Hello AFU – Part 2

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.

reset_command

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_jval and 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 timebase_request and 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 always_ff statement.

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.

low_signals



Driving signals

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 ah_jdone signal.

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

done_wrong

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 jdone.

done_right

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.

Leave a Reply