Skip to content

Commit

Permalink
fix: Handle negative numbers in registers
Browse files Browse the repository at this point in the history
Caused by sweepers initialization and update
  • Loading branch information
alecandido committed Feb 13, 2025
1 parent 57bb82f commit ec44271
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 14 deletions.
9 changes: 7 additions & 2 deletions src/qibolab/_core/instruments/qblox/sequence/loops.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
Reference,
Register,
ResetPh,
Sub,
Wait,
)
from .asm import Registers
Expand Down Expand Up @@ -131,8 +132,12 @@ def _sweep_update(p: Param, channel: ChannelId) -> Block:
"""
return (
Line(
instruction=Add(a=p.reg, b=p.step, destination=p.reg),
comment=f"increment {p.description}",
instruction=(
Add(a=p.reg, b=p.step, destination=p.reg)
if p.step >= 0
else Sub(a=p.reg, b=-p.step, destination=p.reg)
),
comment=f"shift {p.description}",
),
*(
update_instructions(p.kind, p.reg)
Expand Down
76 changes: 64 additions & 12 deletions src/qibolab/_core/instruments/qblox/sequence/transpile.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from typing import Optional, cast

from ..q1asm.ast_ import (
Block,
Instruction,
Expand All @@ -6,6 +8,8 @@
Move,
Program,
Reference,
Register,
Sub,
Wait,
)
from .asm import Registers
Expand All @@ -15,7 +19,7 @@
MAX_WAIT = 2**16 - 1


def long_wait(duration: int) -> Block:
def _long_wait(duration: int) -> Block:
iterations = duration // MAX_WAIT
remainder = duration % MAX_WAIT
register = Registers.wait.value
Expand All @@ -27,25 +31,73 @@ def long_wait(duration: int) -> Block:
]


def decompose(line: Line) -> Block:
if not isinstance(line.instruction, Wait):
return [line]
duration = line.instruction.duration
def _decompose_wait(instr: Wait) -> Optional[Block]:
duration = instr.duration
if not isinstance(duration, int) or duration <= MAX_WAIT:
return None
return _long_wait(duration)


def _negative_move(instr: Move):
"""Compile negative value sets.
Apparently, the only place where negative numbers are not allowed
are registers, otherwise they are handled by the internal compiler.
https://docs.qblox.com/en/main/tutorials/q1asm_tutorials/intermediate/nco_control_adv.html#:~:text=Internally,%20the%20processor%20stores
Thus, we compile instructions setting negative values as suggested:
first setting them to 0, than subtracting the desired amount. This
is more reliable than manually complementing the number, since it
makes no assumption about the registers size.
https://docs.qblox.com/en/main/cluster/troubleshooting.html#:~:text=How%20do%20I%20set%20negative%20numbers
"""
src = cast(int, instr.source)
dest = instr.destination
return [Move(source=0, destination=dest), Sub(a=dest, b=abs(src), destination=dest)]


def _decompose_move(instr: Move) -> Optional[Block]:
src = instr.source
if isinstance(src, Register):
return None
assert isinstance(src, int)
if src >= 0:
return None
return _negative_move(instr)


def _decompose(line: Line) -> Block:
instr = line.instruction
block = (
_decompose_wait(instr)
if isinstance(instr, Wait)
else _decompose_move(instr)
if isinstance(instr, Move)
else None
)

# default
if block is None:
return [line]
wait = long_wait(duration)
assert isinstance(wait[0], Instruction)
return [
Line(instruction=wait[0], label=line.label, comment=line.comment),
*(el for el in wait[1:]),
]

assert isinstance(block[0], Instruction)
return (
[
Line(instruction=block[0], label=line.label, comment=line.comment),
*(el for el in block[1:]),
]
if block is not None
else [line]
)


def transpile(prog: Block) -> Program:
return Program(
elements=[
el if isinstance(el, Line) else Line.instr(el)
for oel in prog
for el in (decompose(oel) if isinstance(oel, Line) else [oel])
for el in (_decompose(oel) if isinstance(oel, Line) else [oel])
]
)

0 comments on commit ec44271

Please sign in to comment.