Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/articles/verify.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
A verification result records how a device behaved against a stated version of the specification, and it confers no compliance status.

> [!Warning]
> Verification writes to device registers. Conformance cannot be established without exercising writes, read-only enforcement and event streams, so there is no read-only mode. Some checks leave the device clock and the operation control register in a changed state, and the run assumes a freshly powered device. Avoid verifying a device that is part of a running experiment.
> Verification writes to device registers. Conformance cannot be established without exercising writes, read-only enforcement and event streams, so there is no read-only mode. Some checks leave the device clock and the operation control register in a changed state, and the run assumes a freshly powered device. An interrupted run can also leave the timestamp register locked until the device is power cycled. Avoid verifying a device that is part of a running experiment.

## Running a verification

Expand Down
75 changes: 72 additions & 3 deletions src/Harp.Toolkit/Verify/Suites/CoreRegisters/R_CLOCK_CONFIG.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ namespace Harp.Toolkit.Verify.Suites;

internal class R_CLOCK_CONFIG : Suite
{
const uint WriteOffsetSeconds = 10;
const string LockNotImplementedMessage = "Setting the lock state had no effect, so the device does not implement the timestamp lock.";
const string ClockRoutingMessage = "The device is repeating or generating the synchronization clock, where the effect of CLK_LOCK on writes is unspecified.";
const ClockConfigurationFlags ClockRouting = ClockConfigurationFlags.ClockRepeater | ClockConfigurationFlags.ClockGenerator;

public override string Description => "Clock Configuration Register Tests";

[HarpTest(Description = "Validates that ClockConfig register is readable.")]
Expand All @@ -13,12 +18,60 @@ public async Task<IResult> IsReadable(VerifyConnection device)
return await RegisterHelpers.AssertReadableAsync(a => device.ReadByteAsync(a), ClockConfiguration.Address, "ClockConfig");
}

[HarpTest(Description = "Validates that the timestamp register accepts a write while CLK_UNLOCK is set.")]
public async Task<IResult> UnlockPermitsTimestampWrite(VerifyConnection device)
{
if (await IsClockRoutingEnabledAsync(device))
return new Result<bool>(false, Status.Skipped, ClockRoutingMessage);

if (!await TrySetClockStateAsync(device, ClockConfigurationFlags.ClockUnlock))
return new Result<bool>(false, Status.Skipped, LockNotImplementedMessage);

var target = await device.ReadTimestampSecondsAsync() + WriteOffsetSeconds;
await device.WriteTimestampSecondsAsync(target);
var readBack = await device.ReadTimestampSecondsAsync();
var elapsedSeconds = (long)readBack - target;
return new AssertionResult(
elapsedSeconds >= 0 && elapsedSeconds <= 1,
x => x
? $"TimestampSeconds accepted {target} while unlocked."
: $"Wrote {target} to TimestampSeconds while unlocked and read it back as {readBack}.");
}

[HarpTest(Description = "Validates that the timestamp register refuses a write while CLK_LOCK is set.")]
public async Task<IResult> LockRefusesTimestampWrite(VerifyConnection device)
{
if (await IsClockRoutingEnabledAsync(device))
return new Result<bool>(false, Status.Skipped, ClockRoutingMessage);

try
{
if (!await TrySetClockStateAsync(device, ClockConfigurationFlags.ClockLock))
return new Result<bool>(false, Status.Skipped, LockNotImplementedMessage);

var target = await device.ReadTimestampSecondsAsync() + WriteOffsetSeconds;
var refused = await RegisterHelpers.IsWriteRejectedAsync(
device, HarpCommand.WriteUInt32(TimestampSeconds.Address, target));
var readBack = await device.ReadTimestampSecondsAsync();
return new AssertionResult(
readBack < target,
x => x
? "TimestampSeconds kept its value while locked, and the write was " +
(refused ? "answered with an error." : "acknowledged without taking effect.")
: $"Wrote {target} to TimestampSeconds while locked and it took the value, reading back as {readBack}.");
}
finally
{
await TrySetClockStateAsync(device, ClockConfigurationFlags.ClockUnlock);
}
}

[HarpTest(Description = "Reports clock synchronization capability: REP_ABLE (bit 3) and GEN_ABLE (bit 4).")]
public async Task<IResult> ReportSyncCapability(VerifyConnection device)
{
var value = await device.ReadByteAsync(ClockConfiguration.Address);
bool repAble = (value & (1 << 3)) != 0;
bool genAble = (value & (1 << 4)) != 0;
var value = (ClockConfigurationFlags)await device.ReadByteAsync(ClockConfiguration.Address);
bool repAble = value.HasFlag(ClockConfigurationFlags.RepeaterCapability);
bool genAble = value.HasFlag(ClockConfigurationFlags.GeneratorCapability);
StringBuilder sb = new StringBuilder("ClockConfig sync capability:");
sb.Append("\n");
sb.Append(repAble ? "Device can repeat clock signal" : "Device cannot repeat clock signal");
Expand All @@ -29,4 +82,20 @@ public async Task<IResult> ReportSyncCapability(VerifyConnection device)
true,
sb.ToString());
}

static async Task<bool> IsClockRoutingEnabledAsync(VerifyConnection device)
{
var configuration = (ClockConfigurationFlags)await device.ReadByteAsync(ClockConfiguration.Address);
return (configuration & ClockRouting) != 0;
}

static async Task<bool> TrySetClockStateAsync(VerifyConnection device, ClockConfigurationFlags state)
{
var write = HarpCommand.WriteByte(ClockConfiguration.Address, (byte)state);
if (await RegisterHelpers.IsWriteRejectedAsync(device, write))
return false;

var configuration = (ClockConfigurationFlags)await device.ReadByteAsync(ClockConfiguration.Address);
return (configuration & state) == state;
}
}
Loading