def __init__(self, panel_desc, led_domain="sync"): self.pd = panel_desc self.gp = GammaParameters(gamma=2.5, bpp=8) self.panel = FramebufferedHUB75Driver(self.pd, led_domain=led_domain, gamma_params=self.gp) self.cpu_rom = Memory(width=16, depth=256, init=Instr.assemble(firmware(self.gp.bpp))) self.cpu_core = CoreFSM(alsru_cls=ALSRU_4LUT, memory=self.cpu_rom)
def __init__(self, panel_shape): # panel shape: physical (width, height) in LEDs # i.e. width is how many pixels to shift out per row # and height is 2**(addr_bits)/2 (assuming two rows are driven at once) self.panel_shape = panel_shape self.ftg = FrameTimingGenerator(panel_shape) self.pbr0 = PixelBuffer(panel_shape) self.cpu_rom = Memory(width=16, depth=256, init=Instr.assemble(firmware())) self.cpu_core = CoreFSM(alsru_cls=ALSRU_4LUT, memory=self.cpu_rom)
def firmware(self, fw=None): log.info("Attach firmware") # compile and attach the firmware if fw is None: raise BuildException("No firmware") fw = Instr.assemble(fw) self.fw = fw if len(fw) > self.mem_size - 4 * 8: # some space for the window stack raise BuildException("Firmware too long") log.info("Firmware is {:d}/{:d} ({:.2f}% of mem) words long".format( len(fw), self.mem_size, 100.0 * (float(len(fw)) / float(self.mem_size)))) self.fw = fw self.memory.init = fw
def __init__(self, panel_desc, led_domain="sync"): self.pd = panel_desc self.gp = GammaParameters(gamma=2.5, bpp=8) self.panel = FramebufferedHUB75Driver(self.pd, led_domain=led_domain, gamma_params=self.gp) self.cpu_rom = Memory(width=16, depth=512, init=Instr.assemble(firmware(self.gp.bpp))) self.cpu_ram = SPRAM() self.cpu_core = CoreFSM(alsru_cls=ALSRU_4LUT) self.uart = uart.SimpleUART( default_divisor=uart.calculate_divisor(12e6, 115200))
def make_firmware(controllers, priming_latches, apu_freq_basic=None, apu_freq_advanced=None): num_controllers = len(controllers) buf_size = calc_buf_size(num_controllers) # convert controllers from list of names to list of absolute register # addresses because that's what the system writes to controller_addrs = [] for controller in controllers: try: addr = controller_name_to_addr[controller] except IndexError: raise ValueError("unknown controller name '{}'".format( controller)) from None controller_addrs.append(addr) if apu_freq_basic is None and apu_freq_advanced is not None: raise ValueError("must set apu basic before advanced") num_priming_latches = len(priming_latches)//num_controllers if len(priming_latches) % num_controllers != 0: raise ValueError("priming latches must have {} words per latch".format( num_controllers)) if num_priming_latches == 0: raise ValueError("must have at least one priming latch") if num_priming_latches > buf_size: raise ValueError("too many priming latches: got {}, max is {}".format( num_priming_latches, buf_size)) fw = [ # start from "reset" (i.e. download is finished) # set up initial register window. we get a free one from the bootloader # (so that we can load a register with the window address), but we can't # keep using it. MOVI(R0, INITIAL_REGISTER_WINDOW), STW(R0), # set UART receive timeout to about 2ms. we can't afford to be waiting! MOVI(R0, int((12e6*(2/1000))/256)), STXA(R0, p_map.uart.w_rt_timer), # same timeout for the status timer, just cause it's already in the # register. once it expires, the correct value will be loaded. STXA(R0, p_map.timer.timer[0].w_value), ] # out of reset, the button registers are all zero, the APU frequency is # 24.607104MHz, and latching is disabled. as long as latching remains # disabled, the frequency won't change and the console will see no buttons # no matter how much it latches. # set the initial APU frequency values if apu_freq_basic is not None: fw.append([ MOVI(R2, int(apu_freq_basic) & 0xFFFF), STXA(R2, p_map.snes.w_apu_freq_basic), ]) if apu_freq_advanced is not None: fw.append([ MOVI(R2, int(apu_freq_advanced) & 0xFFFF), STXA(R2, p_map.snes.w_apu_freq_advanced), ]) # force a latch so the APU clock generator gets updated fw.append(STXA(R2, p_map.snes.w_force_latch)) # load the initial buttons into the registers for controller_i, controller_addr in enumerate(controller_addrs): fw.append([ MOVI(R2, priming_latches[controller_i]), STXA(R2, controller_addr), ]) # now that the registers are loaded, we can turn latching back on. this # setup guarantees the console will transition directly from seeing no # buttons to seeing the first set of buttons once it latches. there can't be # any intermediate states. fw.append([ MOVI(R2, 1), STXA(R2, p_map.snes.w_enable_latch), ]) # initialization is done. let's get the party started! fw.append(J("main_loop")) fw.append(send_status_packet(buf_size)) fw.append(main_loop_body()) fw.append(rx_comm_word()) fw.append(cmd_send_latches(controller_addrs, buf_size)) # define all the variables defs = [0]*len(Vars) # the buffer is primed with some latches so that we can start before # communication gets reestablished. but we put one in the interface at the # beginning defs[Vars.buf_head] = num_priming_latches-1 defs[Vars.stream_pos] = num_priming_latches fw.append([ L("vars"), defs ]) # include all the functions fw.append([ L("handle_error"), f_handle_error(), L("update_interface"), f_update_interface(controller_addrs, buf_size), ]) # header reception is called once so we stick it far away fw.append(rx_header()) # assemble just the code region assembled_fw = Instr.assemble(fw) fw_len = len(assembled_fw) if len(assembled_fw) > FW_MAX_LENGTH: raise ValueError( "firmware length {} is over max of {} by {} words".format( fw_len, FW_MAX_LENGTH, fw_len-FW_MAX_LENGTH)) elif False: print("firmware length {} is under max of {} by {} words".format( fw_len, FW_MAX_LENGTH, FW_MAX_LENGTH-fw_len)) # pad it out until the latch buffer starts assembled_fw.extend([0]*(LATCH_BUF_START-len(assembled_fw))) # then fill it with the priming latches (skipping the one we stuck in the # interface at the beginning) assembled_fw.extend(priming_latches[num_controllers:]) return assembled_fw
Rem("Jump table select"), sel(), ll("exit"), ] if __name__ == "__main__": r = CharPad.SplitChomp() print(r.code()) if False: log.critical("TESTING") console = Console() w = Window() w.req("val") ll = LocalLabels() s.test = "this is a test" w.req(["pad", "value"]) cs = Switch(w, w.val) cs.add(("c", [ll("a")])) cs.add(("r", [ll("b")])) cs.add(("s", [ll("c")])) cs.add((43, [ll("d")])) cs.add((10, [ll("e")])) cs.add((13, [ll("f")])) d = cs.dump() print(d) r = Instr.assemble(d) d = Instr.disassemble(r) print(r) print(d)
L(self.label), # self.ref(self.next), Ref(self.next), self.commname.as_mem(), ADJW(-8), LDW(R6, 0), self.instr(), ADJW(8), JR(R7, 0), ] if __name__ == "__main__": c = MetaCommand.code() print(c) fw = Instr.assemble(c) print(fw) # working jump refs def test(): def jump_table(*args): def relocate(resolver): return [resolver(arg) for arg in args] return relocate def ref(val): def relocate(resolver): return resolver(val)
m = Module() m.submodules.core = core = self.core leds = Cat(platform.request("led", n) for n in range(4)) with m.If(core.o_ext_we & (core.o_bus_addr == 0x0000)): m.d.sync += leds.eq(core.o_ext_data) return m def firmware(): period = 3300000 // (4 * 3) # 4 CPI, 3 instructions return [ MOVI(R7, 0xa), L("blink"), XORI(R7, R7, 0xf), STXA(R7, 0), MOVI(R1, period & 0xffff), MOVI(R2, period >> 16), L("loop"), SUBI(R1, R1, 1), SBBI(R2, R2, 0), JNZ("loop"), J("blink"), ] if __name__ == "__main__": design = BonelessDemo(firmware=Instr.assemble(firmware())) ICE40HX1KBlinkEVNPlatform().build(design, do_program=True)
def make_bootloader(info_words): cleaned_info_words = [int(info_word) & 0xFFFF for info_word in info_words] if len(cleaned_info_words) != 4: raise ValueError("expected exactly four info words") cleaned_info_words.append(0) cleaned_info_words.append(0) cleaned_info_words.append(GATEWARE_VERSION) cleaned_info_words.append(BOOTLOADER_VERSION) fw = [ L("reset"), # start from reset. we are always going to start from reset, so we don't # have to worry about e.g. changing peripherals back to default modes. # set UART receive timeout to about 150ms MOVI(R0, int((12e6 * (150 / 1000)) / 256)), STXA(R0, p_map.uart.w_rt_timer), ] r = RegisterManager("R7:lr R6:comm_word R5:temp R4:cmd_status " "R2:param2 R1:param1 R0:command") fw.append([ L("main_loop"), # clear any UART errors and reset the receive timeout MOVI(r.temp, 0xFFFF), STXA(r.temp, p_map.uart.w_error_clear), L("rx_header_lo"), # wait to get the header low byte (0x5A) # check for UART errors (timeouts, overflows, etc.) LDXA(r.temp, p_map.uart.r_error), AND(r.temp, r.temp, r.temp), # set flags BZ0("main_loop"), # get a new byte and check if it matches. we don't bother checking if we # got anything because it won't match in that case and we just loop # until it does LDXA(r.temp, p_map.uart.r_rx_hi), CMPI(r.temp, 0x5A << 7), BNE("rx_header_lo"), L("rx_header_hi"), # do the same for the header high byte (0x7A) # check for UART errors (timeouts, overflows, etc.) LDXA(r.temp, p_map.uart.r_error), AND(r.temp, r.temp, r.temp), # set flags BZ0("main_loop"), # get a new byte and check if it matches. if we didn't get anything we # try to receive the high byte again. LDXA(r.temp, p_map.uart.r_rx_hi), ADD(r.temp, r.temp, r.temp), BC1("rx_header_hi"), # if we actually got the first byte, go back to looking for the second CMPI(r.temp, 0x5A << 8), BEQ("rx_header_hi"), CMPI(r.temp, 0x7A << 8), BNE("main_loop"), # we have confirmed both header bytes. who knows what the CRC is now. STXA(r.temp, p_map.uart.w_crc_reset), # write something to reset it # receive the four packet words JAL(r.lr, "rx_word"), MOV(r.command, r.comm_word), JAL(r.lr, "rx_word"), MOV(r.param1, r.comm_word), JAL(r.lr, "rx_word"), MOV(r.param2, r.comm_word), JAL(r.lr, "rx_word"), # if the current CRC is x, then CRC(x) = 0, always. we use to reset the # CRC to 0 when we are done sending or receiving. # so, we've received all the data words and the CRC. if everything went # okay, the CRC should now be 0. LDXA(r.temp, p_map.uart.r_crc_value), AND(r.temp, r.temp, r.temp), BZ("crc_ok"), # aw heck, it didn't go okay. send the appropriate error. MOVI(r.cmd_status, 1), J("send_final_response_packet"), L("crc_ok"), # now we need to figure out the command. # the low 8 bits are the length, which is always 2. SUBI(r.command, r.command, 2), # the high 8 bits are the command number, starting from 1 MOVI(r.temp, 0x100), # assume the handler wants to say success MOVI(r.cmd_status, 3), SUB(r.command, r.command, r.temp), # command 1 # hello command: we just need to transmit a success packet back, and the # status is already set accordingly BEQ("send_final_response_packet"), SUB(r.command, r.command, r.temp), # command 2 BEQ("sys_cmd_write_data"), SUB(r.command, r.command, r.temp), # command 3 BEQ("sys_cmd_jump_to_code"), SUB(r.command, r.command, r.temp), # command 4 BEQ("sys_cmd_read_data"), # if the command is unknown or the length is wrong, none of the above # will have matched. send the appropriate error. MOVI(r.cmd_status, 0), # fall through ]) r -= "command" r += "R0:send_lr" fw.append([ L("send_final_response_packet"), # return to main loop MOVR(r.lr, "main_loop"), L("send_response_packet"), # save LR because we need to reuse it to call the tx function MOV(r.send_lr, r.lr), # send out the header first MOVI(r.comm_word, 0x7A5A), JAL(r.lr, "tx_word"), # reset CRC so the packet CRC is calculated correctly STXA(r.send_lr, p_map.uart.w_crc_reset), # we can write anything # the command is always the same: length 1 type 1 MOVI(r.comm_word, 0x0101), JAL(r.lr, "tx_word"), # then the status code MOV(r.comm_word, r.cmd_status), JAL(r.lr, "tx_word"), # last is the CRC, but the CRC of the last byte is still being # calculated right now. reading it this instruction gives garbage. MOV(r.lr, r.send_lr), # so prepare to return to our caller # now we can read and send it (and sending it resets the CRC to 0) LDXA(r.comm_word, p_map.uart.r_crc_value), # tx_word will return back to our caller in r.lr J("tx_word"), ]) r -= "send_lr param2 param1" r += "R2:length R1:dest_addr" # generate random prefix so that we effectively can make local labels lp = "_{}_".format(random.randrange(2**32)) fw.append([ L("sys_cmd_write_data"), # transmit back a success packet. the status is already set correctly. JAL(r.lr, "send_response_packet"), # if the length is 0, we are already done. send the second response. AND(r.length, r.length, r.length), BZ("send_final_response_packet"), L(lp + "write"), JAL(r.lr, "rx_word"), ST(r.comm_word, r.dest_addr, 0), ADDI(r.dest_addr, r.dest_addr, 1), SUBI(r.length, r.length, 1), BNZ(lp + "write"), # receive the CRC too JAL(r.lr, "rx_word"), # now, if everything was correct, the CRC in the UART will be zero LDXA(r.temp, p_map.uart.r_crc_value), AND(r.temp, r.temp, r.temp), BZ("send_final_response_packet"), # it was! (status already = success) # it wasn't. send the appropriate error MOVI(r.cmd_status, 1), J("send_final_response_packet"), ]) r -= "length dest_addr" r += "R2:param2 R1:code_addr" fw.append([ L("sys_cmd_jump_to_code"), # transmit back a success packet. the status is already set correctly. JAL(r.lr, "send_response_packet"), # then jump to the code and let it do its thing JR(r.code_addr, 0), ]) r -= "param2 code_addr" r += "R2:length R1:source_addr" lp = "_{}_".format(random.randrange(2**32)) fw.append([ L("sys_cmd_read_data"), # transmit back a success packet. the status is already set correctly. JAL(r.lr, "send_response_packet"), # if the length is 0, we are already done. send the second response AND(r.length, r.length, r.length), BZ("send_final_response_packet"), L(lp + "read"), LD(r.comm_word, r.source_addr, 0), JAL(r.lr, "tx_word"), ADDI(r.source_addr, r.source_addr, 1), SUBI(r.length, r.length, 1), BNZ(lp + "read"), # send the CRC too (which resets the CRC to 0) LDXA(r.comm_word, p_map.uart.r_crc_value), JAL(r.lr, "tx_word"), # send the final success packet (status is already set) J("send_final_response_packet"), ]) r -= "length source_addr" lp = "_{}_".format(random.randrange(2**32)) fw.append([ L("rx_word"), # check for UART errors (timeouts, overflows, etc.) LDXA(r.temp, p_map.uart.r_error), AND(r.temp, r.temp, r.temp), # set flags BZ0(lp + "error"), # check if we have a new byte LDXA(r.temp, p_map.uart.r_rx_lo), ROLI(r.temp, r.temp, 1), BS1("rx_word"), # we have the low byte in temp L(lp + "rx_hi"), # check again for UART errors LDXA(r.comm_word, p_map.uart.r_error), AND(r.comm_word, r.comm_word, r.comm_word), BZ0(lp + "error"), # and see if we have the high byte yet LDXA(r.comm_word, p_map.uart.r_rx_hi), ADD(r.comm_word, r.comm_word, r.comm_word), BC1(lp + "rx_hi"), # put the bytes together OR(r.comm_word, r.comm_word, r.temp), # and we are done JR(r.lr, 0), L(lp + "error"), # load RX error status code MOVI(r.cmd_status, 2), # and send the error packet (it will return to the main loop) J("send_final_response_packet"), ]) lp = "_{}_".format(random.randrange(2**32)) fw.append([ L("tx_word"), # wait for buffer space LDXA(r.temp, p_map.uart.r_tx_status), ANDI(r.temp, r.temp, 1), BZ0("tx_word"), # then send the low byte STXA(r.comm_word, p_map.uart.w_tx_lo), # and repeat for the high byte L(lp + "tx_hi"), LDXA(r.temp, p_map.uart.r_tx_status), ANDI(r.temp, r.temp, 1), BZ0(lp + "tx_hi"), STXA(r.comm_word, p_map.uart.w_tx_hi), # and we're done JR(r.lr, 0), # WARNING! the CRC is still being calculated, so reading it immediately # after this function returns will return garbage ]) # assemble just the code region assembled_fw = Instr.assemble(fw) fw_len = len(assembled_fw) if len(assembled_fw) > FW_MAX_LENGTH: raise ValueError( "bootrom length {} is over max of {} by {} words".format( fw_len, FW_MAX_LENGTH, fw_len - FW_MAX_LENGTH)) elif False: print("bootrom length {} is under max of {} by {} words".format( fw_len, FW_MAX_LENGTH, FW_MAX_LENGTH - fw_len)) # pad the code region out to line up the info words assembled_fw.extend([0] * (FW_MAX_LENGTH - fw_len)) # glue on the info words assembled_fw.extend(cleaned_info_words) # then the (initially zero) RAM region assembled_fw.extend([0] * 64) return assembled_fw
def boneload(firmware, port, ram_only=True): import serial firmware = Instr.assemble(firmware) print("Connecting...") ser = serial.Serial(port, 115200, timeout=0.5) print("Identifying (reset board please)...") while True: try: ident = _bl_identify(ser) break except Timeout: pass if ident[0] != 1: raise Exception("incompatible version {}".format(ident[0])) print("Identified! Board ID=0x{:02X}, max length={}".format(*ident[1:])) print("Downloading program to RAM...") _bl_write_data(ser, 0, firmware, ident[2]) print("Verifying RAM...") correct_crc = _crc(firmware) calc_crc = _bl_crc(ser, 0, len(firmware)) if calc_crc != correct_crc: raise Exception("verification failed!", calc_crc, correct_crc) if ram_only: print("Beginning execution...") _bl_jump_to_code(ser, 0, 0xFFF) print("Complete!") return return # hard return until the flash is safe print("Awakening flash...") _bl_flash_txn_imm(ser, ident[2], write_data=[0xAB], deassert_cs=True) # it takes a couple microseconds to wake up, which parsing this comment # has already wasted print("Reading flash ID...") _bl_flash_txn_imm(ser, ident[2], write_data=[0x9F]) fid = _bl_flash_txn_imm(ser, ident[2], read_len=3, deassert_cs=True) print("it is: ", end="") for x in fid: print(hex(x), end=" ") print() def _flash_wait(): # wait for BUSY to be off. # start reading BUSY register _bl_flash_txn_imm(ser, ident[2], write_data=[0x05]) while True: # read its current value status = _bl_flash_txn_imm(ser, ident[2], read_len=1)[0] if status & 1 == 0: break # deassert CS and finish command _bl_flash_txn_imm(ser, ident[2], write_data=[], deassert_cs=True) print("Erasing flash sectors...") sector_size = 4096 num_sectors = (len(firmware) * 2 + sector_size - 1) // sector_size # round up for sector in range(num_sectors): # enable write access _bl_flash_txn_imm(ser, ident[2], write_data=[0x06], deassert_cs=True) # do the erase addr = ((sector + 32) * sector_size).to_bytes(3, byteorder="big") _bl_flash_txn_imm(ser, ident[2], write_data=[0x20, *addr], deassert_cs=True) # and wait for it to finish _flash_wait() print("Programming flash pages...") page_size = 256 num_pages = (len(firmware) * 2 + page_size - 1) // page_size # round up for page in range(num_pages): # enable write access _bl_flash_txn_imm(ser, ident[2], write_data=[0x06], deassert_cs=True) # start program operation addr = ((page + 512) * page_size).to_bytes(3, byteorder="big") _bl_flash_txn_imm(ser, ident[2], write_data=[0x02, *addr]) # send bytes from RAM to flash. remember that the flash is in bytes # and we count in words. _bl_flash_txn( ser, page * 128, write_len=min((len(firmware) - (page * 128)) * 2, 256), deassert_cs=True, ) _flash_wait() print("Reloading flash data...") for page in range(num_pages): addr = ((page + 512) * page_size).to_bytes(3, byteorder="big") # start read operation _bl_flash_txn_imm(ser, ident[2], write_data=[0x0B, *addr, 0]) # and actually read the data _bl_flash_txn( ser, page * 128, read_len=min((len(firmware) - (page * 128)) * 2, 256), deassert_cs=True, ) print("Verifying flash...") calc_crc = _bl_crc(ser, 0, len(firmware)) if calc_crc != correct_crc: raise Exception("verification failed!", calc_crc, correct_crc) print("Beginning execution...") _bl_jump_to_code(ser, 0) print("Complete!")
_flash_wait() print("Reloading flash data...") for page in range(num_pages): addr = ((page + 512) * page_size).to_bytes(3, byteorder="big") # start read operation _bl_flash_txn_imm(ser, ident[2], write_data=[0x0B, *addr, 0]) # and actually read the data _bl_flash_txn( ser, page * 128, read_len=min((len(firmware) - (page * 128)) * 2, 256), deassert_cs=True, ) print("Verifying flash...") calc_crc = _bl_crc(ser, 0, len(firmware)) if calc_crc != correct_crc: raise Exception("verification failed!", calc_crc, correct_crc) print("Beginning execution...") _bl_jump_to_code(ser, 0) print("Complete!") if __name__ == "__main__": f = boneload_fw() for w in f: print(Instr.disassemble([w])) x = len(f) print("c:", x, "o:", x - 256, "r:", 512 - x)
from boneless.arch.opcode import Instr from boneless.arch.opcode import * from ..gateware.periph_map import p_map from ..host.bootload import do_bootload # test program to confirm things are working fw = [ L("rx_wait"), LDXA(R1, p_map.uart.r_rx_lo), ROLI(R1, R1, 1), BS1("rx_wait"), MOVR(R0, "hi"), L("tx_wait"), LDXA(R1, p_map.uart.r_tx_status), ANDI(R1, R1, 1), BZ0("tx_wait"), LD(R1, R0, 0), CMPI(R1, 0), BEQ("rx_wait"), STXA(R1, p_map.uart.w_tx_lo), ADDI(R0, R0, 1), J("tx_wait"), L("hi"), list(ord(c) for c in "Hello, world!"), 0 ] # bootload the program to the board on the given port do_bootload(sys.argv[1], Instr.assemble(fw))
def make_firmware(): r = RegisterManager("R6:comm_word R5:rxlr R4:temp " "R2:param2 R1:param1 R0:command") fw = [ # start from "reset" (i.e. download is finished) # we just use the free register window the bootloader gives us # set UART receive timeout to about 100ms. this way reception will be # reset if we don't receive a complete command. MOVI(R0, int((12e6 * (100 / 1000)) / 256)), STXA(R0, p_map.uart.w_rt_timer), L("main_loop"), # clear any UART errors and reset the receive timeout MOVI(r.temp, 0xFFFF), STXA(r.temp, p_map.uart.w_error_clear), L("rx_header_lo"), # wait to get the header low byte (0x5A) # check for UART errors (timeouts, overflows, etc.) LDXA(r.temp, p_map.uart.r_error), AND(r.temp, r.temp, r.temp), # set flags BZ0("main_loop"), # get a new byte and check if it matches. we don't bother checking if we # got anything because it won't match in that case and we just loop # until it does LDXA(r.temp, p_map.uart.r_rx_hi), CMPI(r.temp, 0x5A << 7), BNE("rx_header_lo"), L("rx_header_hi"), # do the same for the header high byte (0x7A) # check for UART errors (timeouts, overflows, etc.) LDXA(r.temp, p_map.uart.r_error), AND(r.temp, r.temp, r.temp), # set flags BZ0("main_loop"), # get a new byte and check if it matches. if we didn't get anything we # try to receive the high byte again. LDXA(r.temp, p_map.uart.r_rx_hi), ADD(r.temp, r.temp, r.temp), BC1("rx_header_hi"), # if we actually got the first byte, go back to looking for the second CMPI(r.temp, 0x5A << 8), BEQ("rx_header_hi"), CMPI(r.temp, 0x7A << 8), BNE("main_loop"), # we have confirmed both header bytes. who knows what the CRC is now. STXA(r.temp, p_map.uart.w_crc_reset), # write something to reset it # receive the command packet JAL(r.rxlr, "rx_comm_word"), MOV(r.command, r.comm_word), JAL(r.rxlr, "rx_comm_word"), MOV(r.param1, r.comm_word), JAL(r.rxlr, "rx_comm_word"), MOV(r.param2, r.comm_word), JAL(r.rxlr, "rx_comm_word"), LDXA(r.temp, p_map.uart.r_crc_value), AND(r.temp, r.temp, r.temp), BNZ("main_loop"), # ignore packets with incorrect CRC CMPI(r.command, 0x0102), BEQ("handle_hello"), CMPI(r.command, 0x2002), BNE("main_loop"), # invalid command # update APU registers with command values STXA(r.param1, p_map.snes.w_apu_freq_basic), STXA(r.param2, p_map.snes.w_apu_freq_advanced), # write something (anything) to force a latch so the clock generator # gets updated with the values we just wrote STXA(R0, p_map.snes.w_force_latch), J("main_loop"), L("handle_hello"), # we got a valid hello. reset into the bootloader. MOVI(R0, 0xFADE), MOVI(R1, 0xDEAD), STXA(R0, p_map.reset_req.w_enable_key_fade), STXA(R1, p_map.reset_req.w_perform_key_dead), J(-1), # hang until it happens L("rx_comm_word"), # check for UART errors (timeouts, overflows, etc.) LDXA(r.temp, p_map.uart.r_error), AND(r.temp, r.temp, r.temp), # set flags BZ0("main_loop"), # ignore errors and reset reception # check if we have a new byte LDXA(r.comm_word, p_map.uart.r_rx_lo), ROLI(r.comm_word, r.comm_word, 1), BS1("rx_comm_word"), L("rcw_hi"), # check again for UART errors LDXA(r.temp, p_map.uart.r_error), AND(r.temp, r.temp, r.temp), BZ0("main_loop"), # and see if we have the high byte yet LDXA(r.temp, p_map.uart.r_rx_hi), ADD(r.temp, r.temp, r.temp), BC1("rcw_hi"), # put the bytes together OR(r.comm_word, r.comm_word, r.temp), # and we are done JR(r.rxlr, 0), ] assembled_fw = Instr.assemble(fw) # don't bother measuring the length, it's assuredly less than 16,384 words return assembled_fw
def boneload_fw(uart_addr=0, spi_addr=16): return Instr.assemble(_bfw_main(uart_addr, spi_addr))