diff --git a/sbus-to-ztex-gateware-migen/sbus_to_fpga_fsm.py b/sbus-to-ztex-gateware-migen/sbus_to_fpga_fsm.py new file mode 100644 index 0000000..cac43c0 --- /dev/null +++ b/sbus-to-ztex-gateware-migen/sbus_to_fpga_fsm.py @@ -0,0 +1,488 @@ + +from migen import * +from migen.fhdl.specials import Tristate + +SIZ_WORD = 0x0 +SIZ_BYTE = 0x1 +SIZ_HWORD = 0x2 +SIZ_EXT = 0x3 +SIZ_BURST4 = 0x4 +SIZ_BURST8 = 0x5 +SIZ_BURST16 = 0x6 +SIZ_BURST2 = 0x7 + +ACK_IDLE = 0x7 +ACK_ERR = 0x6 +ACK_BYTE = 0x5 +ACK_RERUN = 0x4 +ACK_WORD = 0x3 +ACK_DWORD = 0x2 +ACK_HWORD = 0x1 +ACK_RECV = 0x0 + +ADDR_PHYS_HIGH = 27 +ADDR_PHYS_LOW = 0 +ADDR_PFX_HIGH = ADDR_PHYS_HIGH +ADDR_PFX_LOW = 16 ## 64 KiB per prefix +ADDR_PFX_LENGTH = 12 #(1 + ADDR_PFX_HIGH - ADDR_PFX_LOW) +ROM_ADDR_PFX = Signal(12, reset = 0) +WISHBONE_CSR_ADDR_PFX = Signal(12, reset = 4) +USBOHCI_ADDR_PFX = Signal(12, reset = 8) + +def siz_is_word(siz): + return (SIZ_WORD == siz) | (SIZ_BURST2 == siz) | (SIZ_BURST4 == siz) | (SIZ_BURST8 == siz) | (SIZ_BURST16 == siz) + +# FIXME: this doesn't work. Verilog aways use value[0:4] +#def _index_with_wrap(counter, limit_m1, value): +# if (limit_m1 == 0): +# return value[0:4] +# elif (limit_m1 == 1): +# return Cat((value + counter)[0:1], value[1:4]) +# elif (limit_m1 == 3): +# return Cat((value + counter)[0:2], value[2:4]) +# elif (limit_m1 == 7): +# return Cat((value + counter)[0:3], value[3:4]) +# elif (limit_m1 == 15): +# return (value + counter)[0:4] +# return value[0:4] + +def index_with_wrap(counter, limit_m1, value): + return ((value+counter) & limit_m1)[0:4] | (value&(~limit_m1))[0:4] + +# FIXME: this doesn't work. Verilog aways use 1 +def siz_to_burst_size_m1(siz): + if (SIZ_WORD == siz): + return 0 + elif (SIZ_BURST2 == siz): + return 1 + elif (SIZ_BURST4 == siz): + return 3 + elif (SIZ_BURST8 == siz): + return 7 + elif (SIZ_BURST16 == siz): + return 15 + return 1 + +class LedDisplay(Module): + def __init__(self, pads): + n = len(pads) + self.value = Signal(32, reset = 0x18244281) + old_value = Signal(32) + display = Signal(8) + + self.submodules.fsm = fsm = FSM(reset_state="Reset") + time_counter = Signal(32, reset = 0) + blink_counter = Signal(4, reset = 0) + self.comb += pads.eq(display) + fsm.act("Reset", + NextValue(time_counter, 25000000//10), + NextValue(blink_counter, 10), + NextValue(display, 0x00), + NextValue(old_value, self.value), + NextState("Quick")) + fsm.act("Quick", + If (old_value != self.value, + NextState("Reset") + ).Elif(time_counter == 0, + If (blink_counter == 0, + NextValue(time_counter, 25000000//2), + NextValue(display, self.value[0:8]), + NextState("Byte0") + ).Else( + NextValue(display, ~display), + NextValue(time_counter, 25000000//10), + NextValue(blink_counter, blink_counter - 1) + ) + ).Else( + NextValue(time_counter, time_counter - 1) + ) + ) + fsm.act("Byte0", + If (old_value != self.value, + NextState("Reset") + ).Elif(time_counter == 0, + NextValue(time_counter, 25000000//2), + NextValue(display, self.value[8:16]), + NextState("Byte1") + ).Else( + NextValue(time_counter, time_counter - 1) + ) + ) + fsm.act("Byte1", + If (old_value != self.value, + NextState("Reset") + ).Elif(time_counter == 0, + NextValue(time_counter, 25000000//2), + NextValue(display, self.value[16:24]), + NextState("Byte2") + ).Else( + NextValue(time_counter, time_counter - 1) + ) + ) + fsm.act("Byte2", + If (old_value != self.value, + NextState("Reset") + ).Elif(time_counter == 0, + NextValue(time_counter, 25000000//2), + NextValue(display, self.value[24:32]), + NextState("Byte3") + ).Else( + NextValue(time_counter, time_counter - 1) + ) + ) + fsm.act("Byte3", + If (old_value != self.value, + NextState("Reset") + ).Elif(time_counter == 0, + NextValue(time_counter, 25000000//10), + NextValue(blink_counter, 10), + NextValue(display, 0x00), + NextState("Quick") + ).Else( + NextValue(time_counter, time_counter - 1) + ) + ) + +class SBusFPGABus(Module): + def __init__(self, platform, prom, hold_reset, wr_fifo, rd_fifo_addr, rd_fifo_data): + self.platform = platform + self.hold_reset = hold_reset + self.wr_fifo = wr_fifo + self.rd_fifo_addr = rd_fifo_addr + self.rd_fifo_data = rd_fifo_data + + #self.submodules.led_display = LedDisplay(pads=platform.request_all("user_led")) + + #pad_SBUS_3V3_CLK = platform.request("SBUS_3V3_CLK") + pad_SBUS_3V3_ASs = platform.request("SBUS_3V3_ASs") + pad_SBUS_3V3_BGs = platform.request("SBUS_3V3_BGs") + pad_SBUS_3V3_BRs = platform.request("SBUS_3V3_BRs") + pad_SBUS_3V3_ERRs = platform.request("SBUS_3V3_ERRs") + pad_SBUS_DATA_OE_LED = platform.request("SBUS_DATA_OE_LED") + ###pad_SBUS_DATA_OE_LED_2 = platform.request("SBUS_DATA_OE_LED_2") + #pad_SBUS_3V3_RSTs = platform.request("SBUS_3V3_RSTs") + pad_SBUS_3V3_SELs = platform.request("SBUS_3V3_SELs") + #pad_SBUS_3V3_INT1s = platform.request("SBUS_3V3_INT1s") + #pad_SBUS_3V3_INT7s = platform.request("SBUS_3V3_INT7s") + pad_SBUS_3V3_PPRD = platform.request("SBUS_3V3_PPRD") + pad_SBUS_OE = platform.request("SBUS_OE") + pad_SBUS_3V3_ACKs = platform.request("SBUS_3V3_ACKs") + pad_SBUS_3V3_SIZ = platform.request("SBUS_3V3_SIZ") + pad_SBUS_3V3_D = platform.request("SBUS_3V3_D") + pad_SBUS_3V3_PA = platform.request("SBUS_3V3_PA") + assert len(pad_SBUS_3V3_D) == 32, "len(pad_SBUS_3V3_D) should be 32" + assert len(pad_SBUS_3V3_PA) == 28, "len(pad_SBUS_3V3_PA) should be 28" + + sbus_oe_data = Signal(reset=0) + sbus_oe_slave_in = Signal(reset=0) + sbus_oe_master_in = Signal(reset=0) + sbus_oe_int1 = Signal(reset=0) + sbus_oe_int7 = Signal(reset=0) + sbus_oe_master_br = Signal(reset=0) + + sbus_last_pa = Signal(28) + burst_index = Signal(4) + burst_counter = Signal(4) + burst_limit_m1 = Signal(4) + + #SBUS_3V3_CLK = Signal() + SBUS_3V3_ASs_i = Signal() + self.comb += SBUS_3V3_ASs_i.eq(pad_SBUS_3V3_ASs) + SBUS_3V3_BGs_i = Signal() + self.comb += SBUS_3V3_BGs_i.eq(pad_SBUS_3V3_BGs) + SBUS_3V3_BRs_o = Signal(reset=1) + self.specials += Tristate(pad_SBUS_3V3_BRs, SBUS_3V3_BRs_o, sbus_oe_master_br, None) + SBUS_3V3_ERRs_i = Signal() + SBUS_3V3_ERRs_o = Signal() + self.specials += Tristate(pad_SBUS_3V3_ERRs, SBUS_3V3_ERRs_o, sbus_oe_master_in, SBUS_3V3_ERRs_i) + SBUS_DATA_OE_LED_o = Signal() + self.comb += pad_SBUS_DATA_OE_LED.eq(SBUS_DATA_OE_LED_o) + ###SBUS_DATA_OE_LED_2_o = Signal() + ###self.comb += pad_SBUS_DATA_OE_LED_2.eq(SBUS_DATA_OE_LED_2_o) + #SBUS_3V3_RSTs = Signal() + SBUS_3V3_SELs_i = Signal() + self.comb += SBUS_3V3_SELs_i.eq(pad_SBUS_3V3_SELs) + #SBUS_3V3_INT1s_o = Signal(reset=1) + #self.specials += Tristate(pad_SBUS_3V3_INT1s, SBUS_3V3_INT1s_o, sbus_oe_int1, None) + #SBUS_3V3_INT7s_o = Signal(reset=1) + #self.specials += Tristate(pad_SBUS_3V3_INT7s, SBUS_3V3_INT7s_o, sbus_oe_int7, None) + SBUS_3V3_PPRD_i = Signal() + SBUS_3V3_PPRD_o = Signal() + self.specials += Tristate(pad_SBUS_3V3_PPRD, SBUS_3V3_PPRD_o, sbus_oe_slave_in, SBUS_3V3_PPRD_i) + #SBUS_OE_o = Signal() + self.comb += pad_SBUS_OE.eq(self.hold_reset) + SBUS_3V3_ACKs_i = Signal(3) + SBUS_3V3_ACKs_o = Signal(3) + self.specials += Tristate(pad_SBUS_3V3_ACKs, SBUS_3V3_ACKs_o, sbus_oe_master_in, SBUS_3V3_ACKs_i) + SBUS_3V3_SIZ_i = Signal(3) + SBUS_3V3_SIZ_o = Signal(3) + self.specials += Tristate(pad_SBUS_3V3_SIZ, SBUS_3V3_SIZ_o, sbus_oe_slave_in, SBUS_3V3_SIZ_i) + SBUS_3V3_D_i = Signal(32) + SBUS_3V3_D_o = Signal(32) + self.specials += Tristate(pad_SBUS_3V3_D, SBUS_3V3_D_o, sbus_oe_data, SBUS_3V3_D_i) + SBUS_3V3_PA_i = Signal(28) + self.comb += SBUS_3V3_PA_i.eq(pad_SBUS_3V3_PA) + + p_data = Signal(32) # data to read/write + + data_read_addr = Signal(30) # first addr of req. when reading from WB + data_read_enable = Signal() # start enqueuing req. to read from WB + data_read_timeout = Signal(7) + data_read_stale = Signal(5, reset = 0) + + # clean the read FIFO from stale data + self.submodules.cleaning_fsm = cleaning_fsm = FSM(reset_state="Reset") + cleaning_fsm.act("Reset", + NextState("Idle")) + cleaning_fsm.act("Idle", + If(rd_fifo_data.readable & (data_read_stale != 0), + rd_fifo_data.re.eq(1), + NextValue(data_read_stale, data_read_stale - 1))) + self.comb += SBUS_DATA_OE_LED_o.eq(data_read_stale != 0) + + self.submodules.slave_fsm = slave_fsm = FSM(reset_state="Reset") + + slave_fsm.act("Reset", + NextValue(sbus_oe_int1, 0), + NextValue(sbus_oe_int7, 0), + NextValue(sbus_oe_data, 0), + NextValue(sbus_oe_slave_in, 0), + NextValue(sbus_oe_master_in, 0), + NextValue(sbus_oe_master_br, 0), + NextValue(p_data, 0), + NextState("Start") + ) + slave_fsm.act("Start", + NextValue(sbus_oe_int1, 0), + NextValue(sbus_oe_int7, 0), + NextValue(sbus_oe_data, 0), + NextValue(sbus_oe_slave_in, 0), + NextValue(sbus_oe_master_in, 0), + NextValue(sbus_oe_master_br, 0), + NextValue(p_data, 0), + If((self.hold_reset == 0), NextState("Idle")) + ) + slave_fsm.act("Idle", + If(((SBUS_3V3_SELs_i == 0) & + (SBUS_3V3_ASs_i == 0) & + (data_read_stale != 0)), ## refuse access until we've cleaned up the mess + NextValue(sbus_oe_master_in, 1), + NextValue(SBUS_3V3_ACKs_o, ACK_RERUN), + NextValue(SBUS_3V3_ERRs_o, 1), + NextState("Slave_Error") + ).Elif(((SBUS_3V3_SELs_i == 0) & + (SBUS_3V3_ASs_i == 0) & + (siz_is_word(SBUS_3V3_SIZ_i)) & + (SBUS_3V3_PPRD_i == 1) & + (SBUS_3V3_PA_i[0:2] == 0)), + NextValue(sbus_oe_master_in, 1), + NextValue(sbus_last_pa, SBUS_3V3_PA_i), + NextValue(burst_counter, 0), + Case(SBUS_3V3_SIZ_i, { + SIZ_WORD: NextValue(burst_limit_m1, 0), + SIZ_BURST2: NextValue(burst_limit_m1, 1), + SIZ_BURST4: NextValue(burst_limit_m1, 3), + SIZ_BURST8: NextValue(burst_limit_m1, 7), + SIZ_BURST16: NextValue(burst_limit_m1, 15)}), + If((SBUS_3V3_PA_i[ADDR_PFX_LOW:ADDR_PFX_LOW+ADDR_PFX_LENGTH] == ROM_ADDR_PFX), + NextValue(SBUS_3V3_ACKs_o, ACK_WORD), + NextValue(SBUS_3V3_ERRs_o, 1), + NextValue(p_data, prom[SBUS_3V3_PA_i[ADDR_PHYS_LOW+2:ADDR_PFX_LOW]]), + NextState("Slave_Ack_Read_Prom_Burst") + ).Elif(((SBUS_3V3_PA_i[ADDR_PFX_LOW:ADDR_PFX_LOW+ADDR_PFX_LENGTH] == WISHBONE_CSR_ADDR_PFX) | + (SBUS_3V3_PA_i[ADDR_PFX_LOW:ADDR_PFX_LOW+ADDR_PFX_LENGTH] == USBOHCI_ADDR_PFX)), + NextValue(SBUS_3V3_ACKs_o, ACK_IDLE), # need to wait for data, don't ACK yet + NextValue(SBUS_3V3_ERRs_o, 1), + NextValue(p_data, 0xDEADBEEF), + NextValue(data_read_addr, (Cat(SBUS_3V3_PA_i[2:], Signal(4, reset=0)))), # enqueue all the request to the wishbone + NextValue(data_read_enable, 1), # enqueue all the request to the wishbone + NextValue(data_read_timeout, 0xFF), + NextState("Slave_Ack_Read_Reg_Burst_Wait_For_Data") + ).Else( + NextValue(SBUS_3V3_ACKs_o, ACK_ERR), + NextValue(SBUS_3V3_ERRs_o, 1), + NextState("Slave_Error") + ) + ).Elif(((SBUS_3V3_SELs_i == 0) & + (SBUS_3V3_ASs_i == 0) & + (SIZ_BYTE == SBUS_3V3_SIZ_i) & + (SBUS_3V3_PPRD_i == 1)), + NextValue(sbus_oe_master_in, 1), + NextValue(sbus_last_pa, SBUS_3V3_PA_i), + If((SBUS_3V3_PA_i[ADDR_PFX_LOW:ADDR_PFX_LOW+ADDR_PFX_LENGTH] == ROM_ADDR_PFX), + NextValue(SBUS_3V3_ACKs_o, ACK_BYTE), + NextValue(SBUS_3V3_ERRs_o, 1), + NextValue(p_data, prom[SBUS_3V3_PA_i[ADDR_PHYS_LOW+2:ADDR_PFX_LOW]]), + NextState("Slave_Ack_Read_Prom_Byte") + ).Else( + #NextValue(self.led_display.value, Cat(SBUS_3V3_PA_i, Signal(2, reset = 2), SBUS_3V3_PA_i[1:2], SBUS_3V3_PPRD_i)), + NextValue(SBUS_3V3_ACKs_o, ACK_ERR), + NextValue(SBUS_3V3_ERRs_o, 1), + NextState("Slave_Error") + ) + ).Elif(((SBUS_3V3_SELs_i == 0) & + (SBUS_3V3_ASs_i == 0) & + (siz_is_word(SBUS_3V3_SIZ_i)) & + (SBUS_3V3_PPRD_i == 0) & + (SBUS_3V3_PA_i[0:2] == 0) & + (self.wr_fifo.writable)), # maybe we should check for enough space? not that we'll encounter write burst... + NextValue(sbus_oe_master_in, 1), + NextValue(sbus_last_pa, SBUS_3V3_PA_i), + NextValue(burst_counter, 0), + Case(SBUS_3V3_SIZ_i, { + SIZ_WORD: NextValue(burst_limit_m1, 0), + SIZ_BURST2: NextValue(burst_limit_m1, 1), + SIZ_BURST4: NextValue(burst_limit_m1, 3), + SIZ_BURST8: NextValue(burst_limit_m1, 7), + SIZ_BURST16: NextValue(burst_limit_m1, 15)}), + If(((SBUS_3V3_PA_i[ADDR_PFX_LOW:ADDR_PFX_LOW+ADDR_PFX_LENGTH] == WISHBONE_CSR_ADDR_PFX) | + (SBUS_3V3_PA_i[ADDR_PFX_LOW:ADDR_PFX_LOW+ADDR_PFX_LENGTH] == USBOHCI_ADDR_PFX)), + NextValue(SBUS_3V3_ACKs_o, ACK_WORD), + NextValue(SBUS_3V3_ERRs_o, 1), + NextState("Slave_Ack_Reg_Write_Burst") + ).Else( + NextValue(SBUS_3V3_ACKs_o, ACK_ERR), + NextValue(SBUS_3V3_ERRs_o, 1), + NextState("Slave_Error") + ) + ) + ) + # ##### READ ##### + slave_fsm.act("Slave_Ack_Read_Prom_Burst", + NextValue(sbus_oe_data, 1), + NextValue(SBUS_3V3_D_o, p_data), + NextValue(p_data, prom[Cat(index_with_wrap((burst_counter+1), burst_limit_m1, sbus_last_pa[ADDR_PHYS_LOW+2:ADDR_PHYS_LOW+6]), sbus_last_pa[ADDR_PHYS_LOW+6:ADDR_PFX_LOW])]), + If((burst_counter == burst_limit_m1), + NextValue(SBUS_3V3_ACKs_o, ACK_IDLE), + NextState("Slave_Do_Read") + ).Else( + NextValue(SBUS_3V3_ACKs_o, ACK_WORD), + NextValue(burst_counter, burst_counter + 1) + ) + ) + slave_fsm.act("Slave_Ack_Read_Prom_Byte", + NextValue(sbus_oe_data, 1), + If((sbus_last_pa[0:2] == 0x0), + NextValue(SBUS_3V3_D_o, Cat(Signal(24), p_data[24:32])) + ).Elif((sbus_last_pa[0:2] == 0x1), + NextValue(SBUS_3V3_D_o, Cat(Signal(24), p_data[16:24])) + ).Elif((sbus_last_pa[0:2] == 0x2), + NextValue(SBUS_3V3_D_o, Cat(Signal(24), p_data[ 8:16])) + ).Elif((sbus_last_pa[0:2] == 0x3), + NextValue(SBUS_3V3_D_o, Cat(Signal(24), p_data[ 0: 8])) + ), + NextState("Slave_Do_Read") + ) + slave_fsm.act("Slave_Do_Read", + NextValue(sbus_oe_int1, 0), + NextValue(sbus_oe_int7, 0), + NextValue(sbus_oe_data, 0), + NextValue(sbus_oe_slave_in, 0), + NextValue(sbus_oe_master_in, 0), + NextValue(sbus_oe_master_br, 0), + If((SBUS_3V3_ASs_i == 1), + NextState("Idle") + ) + ) + slave_fsm.act("Slave_Ack_Read_Reg_Burst", + NextValue(sbus_oe_data, 1), + NextValue(SBUS_3V3_D_o, p_data), + If((burst_counter == burst_limit_m1), + NextValue(SBUS_3V3_ACKs_o, ACK_IDLE), + NextState("Slave_Do_Read") + ).Else( + NextValue(burst_counter, burst_counter + 1), + If(rd_fifo_data.readable, + If(rd_fifo_data.dout[32] == 0, + NextValue(p_data, rd_fifo_data.dout), + rd_fifo_data.re.eq(1), + NextValue(SBUS_3V3_ACKs_o, ACK_WORD) + ).Else( + rd_fifo_data.re.eq(1), + NextValue(SBUS_3V3_ACKs_o, ACK_RERUN), + NextValue(data_read_stale, burst_limit_m1 - burst_counter), + NextState("Slave_Do_Read"), + ) + ).Else( + NextValue(SBUS_3V3_ACKs_o, ACK_IDLE), + NextState("Slave_Ack_Read_Reg_Burst_Wait_For_Data") + ) + ) + ) + slave_fsm.act("Slave_Ack_Read_Reg_Burst_Wait_For_Data", + NextValue(data_read_timeout, data_read_timeout - 1), + If(rd_fifo_data.readable, + NextValue(p_data, rd_fifo_data.dout), + rd_fifo_data.re.eq(1), + NextValue(SBUS_3V3_ACKs_o, ACK_WORD), + NextState("Slave_Ack_Read_Reg_Burst") + ).Elif(data_read_timeout == 0, + NextValue(SBUS_3V3_ACKs_o, ACK_RERUN), + NextValue(data_read_stale, 1 + burst_limit_m1 - burst_counter), + NextState("Slave_Do_Read") + ) + ) + # ##### WRITE ##### + slave_fsm.act("Slave_Ack_Reg_Write_Burst", + self.wr_fifo.din.eq(Cat(index_with_wrap(burst_counter, burst_limit_m1, sbus_last_pa[ADDR_PHYS_LOW+2:ADDR_PHYS_LOW+6]), # 4 bits, adr FIXME + sbus_last_pa[ADDR_PHYS_LOW+6:ADDR_PFX_LOW], # 10 bits, adr + sbus_last_pa[ADDR_PFX_LOW:ADDR_PFX_LOW+ADDR_PFX_LENGTH], # 12 bits, adr + Signal(4, reset = 0), # 4 bits, adr (could be removed) + SBUS_3V3_D_i)), # 32 bits, data + self.wr_fifo.we.eq(1), + If((burst_counter == burst_limit_m1), + NextValue(SBUS_3V3_ACKs_o, ACK_IDLE), + NextState("Slave_Ack_Reg_Write_Final") + ).Else( + NextValue(SBUS_3V3_ACKs_o, ACK_WORD), + NextValue(burst_counter, burst_counter + 1) + ) + ) + slave_fsm.act("Slave_Ack_Reg_Write_Final", + NextValue(sbus_oe_int1, 0), + NextValue(sbus_oe_int7, 0), + NextValue(sbus_oe_data, 0), + NextValue(sbus_oe_slave_in, 0), + NextValue(sbus_oe_master_in, 0), + NextValue(sbus_oe_master_br, 0), + If((SBUS_3V3_ASs_i == 1), + NextState("Idle") + ) + ) + # ##### ERROR ##### + slave_fsm.act("Slave_Error", + NextValue(sbus_oe_int1, 0), + NextValue(sbus_oe_int7, 0), + NextValue(sbus_oe_data, 0), + NextValue(sbus_oe_slave_in, 0), + NextValue(sbus_oe_master_in, 0), + NextValue(sbus_oe_master_br, 0), + If((SBUS_3V3_ASs_i == 1), + NextState("Idle") + ) + ) + + self.submodules.request_fsm = request_fsm = FSM(reset_state="Reset") + request_fsm.act("Reset", + NextState("Idle") + ) + request_fsm.act("Idle", + If(data_read_enable, + NextValue(data_read_enable, 0), + self.rd_fifo_addr.we.eq(1), + self.rd_fifo_addr.din.eq(data_read_addr), + If (burst_limit_m1 != burst_counter, # 0 the first time + NextValue(burst_counter, burst_counter + 1), + NextState("Queue") + ) + ) + ) + request_fsm.act("Queue", + self.rd_fifo_addr.we.eq(1), + self.rd_fifo_addr.din.eq(Cat(index_with_wrap(burst_counter, burst_limit_m1, data_read_addr[0:4]), data_read_addr[4:])), + If (burst_limit_m1 != burst_counter, + NextValue(burst_counter, burst_counter + 1), + ).Else( + NextState("Idle") + ) + ) diff --git a/sbus-to-ztex-gateware-migen/sbus_to_fpga_soc.py b/sbus-to-ztex-gateware-migen/sbus_to_fpga_soc.py new file mode 100644 index 0000000..89323e8 --- /dev/null +++ b/sbus-to-ztex-gateware-migen/sbus_to_fpga_soc.py @@ -0,0 +1,196 @@ +import os +import argparse +from migen import * +import litex +from litex.build.generic_platform import * +from litex.build.xilinx.vivado import vivado_build_args, vivado_build_argdict +from litex.soc.integration.soc import * +from litex.soc.integration.soc_core import * +from litex.soc.integration.builder import * +from litex.soc.cores.clock import * +from litex.soc.cores.led import LedChaser +from litex_boards.platforms import ztex213 +from migen.genlib.fifo import * + +from sbus_to_fpga_fsm import *; +from sbus_to_fpga_wishbone import *; + +_sbus_sbus = [ + ("SBUS_3V3_CLK", 0, Pins("D15"), IOStandard("lvttl")), + ("SBUS_3V3_ASs", 0, Pins("T4"), IOStandard("lvttl")), + ("SBUS_3V3_BGs", 0, Pins("T6"), IOStandard("lvttl")), + ("SBUS_3V3_BRs", 0, Pins("R6"), IOStandard("lvttl")), + ("SBUS_3V3_ERRs", 0, Pins("V2"), IOStandard("lvttl")), + ("SBUS_DATA_OE_LED", 0, Pins("U1"), IOStandard("lvttl")), + ("SBUS_DATA_OE_LED_2", 0, Pins("T3"), IOStandard("lvttl")), + ("SBUS_3V3_RSTs", 0, Pins("U2"), IOStandard("lvttl")), + ("SBUS_3V3_SELs", 0, Pins("K6"), IOStandard("lvttl")), + ("SBUS_3V3_INT1s", 0, Pins("R3"), IOStandard("lvttl")), + ("SBUS_3V3_INT7s", 0, Pins("N5"), IOStandard("lvttl")), + ("SBUS_3V3_PPRD", 0, Pins("N6"), IOStandard("lvttl")), + ("SBUS_OE", 0, Pins("P5"), IOStandard("lvttl")), + ("SBUS_3V3_ACKs", 0, Pins("M6 L6 N4"), IOStandard("lvttl")), + ("SBUS_3V3_SIZ", 0, Pins("R7 U3 V1"), IOStandard("lvttl")), + ("SBUS_3V3_D", 0, Pins("J18 K16 J17 K15 K13 J15 J13 J14 H14 H17 G14 G17 G16 G18 H16 F18 F16 E18 F15 D18 E17 G13 D17 F13 F14 E16 E15 C17 C16 A18 B18 C15"), IOStandard("lvttl")), + ("SBUS_3V3_PA", 0, Pins("B16 B17 D14 C14 D12 A16 A15 B14 B13 B12 C12 A14 A13 B11 A11 M4 R2 M3 P2 M2 N2 K5 N1 L4 M1 L3 L1 K3"), IOStandard("lvttl")), +] + +_usb_io = [ + ("usb", 0, + Subsignal("dp", Pins("E3")), # Serial TX + Subsignal("dm", Pins("F3")), # Serial RX + IOStandard("LVCMOS33")) +] +# CRG ---------------------------------------------------------------------------------------------- + +class _CRG(Module): + def __init__(self, platform, sys_clk_freq): + self.clock_domains.cd_sys = ClockDomain() # 100 MHz PLL, reset'ed by SBus, SoC/Wishbone main clock + self.clock_domains.cd_native = ClockDomain(reset_less=True) # 48MHz native, non-reset'ed (for power-on long delay, never reset) + self.clock_domains.cd_sbus = ClockDomain() # 16.67-25 MHz SBus, reset'ed by SBus, native SBus clock domain + self.clock_domains.cd_por = ClockDomain() # 48 MHz native, reset'ed by SBus, power-on-reset timer + self.clock_domains.cd_usb = ClockDomain() # 48 MHZ PLL, reset'ed by SBus, for USB controller + + # # # + clk48 = platform.request("clk48") + self.cd_native.clk = clk48 + clk_sbus = platform.request("SBUS_3V3_CLK") + self.cd_sbus.clk = clk_sbus + rst_sbus = platform.request("SBUS_3V3_RSTs") + + self.comb += self.cd_sbus.rst.eq(~rst_sbus) + + self.submodules.pll = pll = S7MMCM(speedgrade=-1) + pll.register_clkin(clk48, 48e6) + pll.create_clkout(self.cd_sys, sys_clk_freq) + + platform.add_false_path_constraints(self.cd_native.clk, self.cd_sbus.clk) + platform.add_false_path_constraints(self.cd_sys.clk, self.cd_sbus.clk) + platform.add_false_path_constraints(self.cd_sbus.clk, self.cd_native.clk) + platform.add_false_path_constraints(self.cd_sbus.clk, self.cd_sys.clk) + + # Power on reset, reset propagate from SBus to SYS + por_count = Signal(16, reset=2**16-1) + por_done = Signal() + self.comb += self.cd_por.clk.eq(clk48) + self.comb += por_done.eq(por_count == 0) + self.sync.por += If(~por_done, por_count.eq(por_count - 1)) + self.comb += pll.reset.eq(~por_done | ~rst_sbus) + + # USB + self.submodules.usb_pll = usb_pll = S7MMCM(speedgrade=-1) + self.comb += usb_pll.reset.eq(~por_done | ~rst_sbus) + usb_pll.register_clkin(clk48, 48e6) + usb_pll.create_clkout(self.cd_usb, 48e6, margin = 0) + platform.add_false_path_constraints(self.cd_sys.clk, self.cd_usb.clk) + +class SBusFPGA(SoCCore): + def __init__(self, **kwargs): + + kwargs["cpu_type"] = "None" + kwargs["integrated_sram_size"] = 0 + kwargs["with_uart"] = False + kwargs["with_timer"] = False + + self.sys_clk_freq = sys_clk_freq = 100e6 + + self.platform = platform = ztex213.Platform(variant="ztex2.13a", expansion="sbus") + self.platform.add_extension(_sbus_sbus) + self.platform.add_extension(_usb_io) + SoCCore.__init__(self, platform=platform, sys_clk_freq=sys_clk_freq, clk_freq=sys_clk_freq, **kwargs) + wb_mem_map = { + "prom": 0x00000000, + "csr" : 0x00040000, + "usb_host": 0x00080000, + } + self.mem_map.update(wb_mem_map) + self.submodules.crg = _CRG(platform=platform, sys_clk_freq=sys_clk_freq) + self.platform.add_period_constraint(self.platform.lookup_request("SBUS_3V3_CLK", loose=True), 1e9/25e6) # SBus max + + self.submodules.leds = LedChaser( + pads = platform.request_all("user_led"), + sys_clk_freq = sys_clk_freq) + self.add_csr("leds") + + self.add_usb_host(pads=platform.request("usb"), usb_clk_freq=48e6) + #self.comb += self.cpu.interrupt[16].eq(self.usb_host.interrupt) #fixme: need to deal with interrupts + + prom_file = "prom_migen.fc" + prom_data = soc_core.get_mem_data(prom_file, "big") + prom = Array(prom_data) + #print("\n****************************************\n") + #for i in range(len(prom)): + # print(hex(prom[i])) + #print("\n****************************************\n") + self.add_ram("prom", origin=self.mem_map["prom"], size=2**16, contents=prom_data, mode="r") + #getattr(self,"prom").mem.init = prom_data + #getattr(self,"prom").mem.depth = 2**14 + + # don't enable anything on the SBus side for 20 seconds after power up + # this avoids FPGA initialization messing with the cold boot process + # requires us to reset the SPARCstation afterward so the FPGA board + # is properly identified + # This is in the 'native' ClockDomain that is never reset + hold_reset_ctr = Signal(30, reset=960000000) + self.sync.native += If(hold_reset_ctr>0, hold_reset_ctr.eq(hold_reset_ctr - 1)) + hold_reset = Signal(reset=1) + self.comb += hold_reset.eq(~(hold_reset_ctr == 0)) + + + # FIFO to send data & address from SBus to the Wishbone + sbus_to_wishbone_wr_fifo = AsyncFIFOBuffered(width=32+30, depth=16) + sbus_to_wishbone_wr_fifo = ClockDomainsRenamer({"write": "sbus", "read": "sys"})(sbus_to_wishbone_wr_fifo) + self.submodules += sbus_to_wishbone_wr_fifo + + # FIFOs to send address / receive data from SBus to the Wishbone + sbus_to_wishbone_rd_fifo_addr = AsyncFIFOBuffered(width=30, depth=16) + sbus_to_wishbone_rd_fifo_addr = ClockDomainsRenamer({"write": "sbus", "read": "sys"})(sbus_to_wishbone_rd_fifo_addr) + self.submodules += sbus_to_wishbone_rd_fifo_addr + sbus_to_wishbone_rd_fifo_data = AsyncFIFOBuffered(width=32+1, depth=16) + sbus_to_wishbone_rd_fifo_data = ClockDomainsRenamer({"write": "sys", "read": "sbus"})(sbus_to_wishbone_rd_fifo_data) + self.submodules += sbus_to_wishbone_rd_fifo_data + + # SBus to Wishbone FSM, 'Slave' on the SBus side, 'Master' on the Wishbone side + self.submodules.sbus_to_wishbone = SBusToWishbone(platform=self.platform, + wr_fifo=sbus_to_wishbone_wr_fifo, + rd_fifo_addr=sbus_to_wishbone_rd_fifo_addr, + rd_fifo_data=sbus_to_wishbone_rd_fifo_data, + wishbone=wishbone.Interface(data_width=self.bus.data_width)) + + _sbus_bus = SBusFPGABus(platform=self.platform, + prom=prom, + hold_reset=hold_reset, + wr_fifo=sbus_to_wishbone_wr_fifo, + rd_fifo_addr=sbus_to_wishbone_rd_fifo_addr, + rd_fifo_data=sbus_to_wishbone_rd_fifo_data,) + self.submodules.sbus_bus = ClockDomainsRenamer("sbus")(_sbus_bus) + + self.bus.add_master(name="SBusBridgeToWishbone", master=self.sbus_to_wishbone.wishbone) + + # self.soc = Module() + # self.soc.mem_regions = self.mem_regions = {} + # region = litex.soc.integration.soc.SoCRegion(origin=0x0, size=0x0) + # region.length = 0 + # self.mem_regions['csr'] = region + # self.soc.constants = self.constants = {} + # self.soc.csr_regions = self.csr_regions = {} + # self.soc.cpu_type = self.cpu_type = None + +# def do_finalize(self): +# self.platform.add_period_constraint(self.platform.lookup_request("SBUS_3V3_CLK", loose=True), 1e9/25e6) + +def main(): + parser = argparse.ArgumentParser(description="SbusFPGA") + parser.add_argument("--build", action="store_true", help="Build bitstream") + builder_args(parser) + vivado_build_args(parser) + args = parser.parse_args() + + soc = SBusFPGA(**soc_core_argdict(args)) + #soc.add_uart(name="uart", baudrate=115200, fifo_depth=16) + + builder = Builder(soc, **builder_argdict(args)) + builder.build(**vivado_build_argdict(args), run=args.build) + +if __name__ == "__main__": + main() diff --git a/sbus-to-ztex-gateware-migen/sbus_to_fpga_wishbone.py b/sbus-to-ztex-gateware-migen/sbus_to_fpga_wishbone.py index 33cb1ef..73132bf 100644 --- a/sbus-to-ztex-gateware-migen/sbus_to_fpga_wishbone.py +++ b/sbus-to-ztex-gateware-migen/sbus_to_fpga_wishbone.py @@ -3,62 +3,85 @@ from migen import * from litex.soc.interconnect import wishbone class SBusToWishbone(Module): - def __init__(self, wr_fifo, rd_fifo_addr, rd_fifo_data, wishbone): + def __init__(self, platform, wr_fifo, rd_fifo_addr, rd_fifo_data, wishbone): + self.platform = platform self.wr_fifo = wr_fifo self.rd_fifo_addr = rd_fifo_addr self.rd_fifo_data = rd_fifo_data self.wishbone = wishbone + + pad_SBUS_DATA_OE_LED_2 = platform.request("SBUS_DATA_OE_LED_2") + SBUS_DATA_OE_LED_2_o = Signal() + self.comb += pad_SBUS_DATA_OE_LED_2.eq(SBUS_DATA_OE_LED_2_o) data = Signal(32) adr = Signal(30) + timeout = Signal(7) # ##### FSM: write to WB ##### self.submodules.fsm = fsm = FSM(reset_state="Reset") fsm.act("Reset", + self.wishbone.we.eq(0), + self.wishbone.cyc.eq(0), + self.wishbone.stb.eq(0), + NextState("Idle") + ) + fsm.act("Idle", + If (rd_fifo_addr.readable & ~self.wishbone.cyc & self.rd_fifo_data.writable, + rd_fifo_addr.re.eq(1), + NextValue(adr, self.rd_fifo_addr.dout[0:30]), + NextValue(timeout, 127), + NextState("Read") + ).Elif(self.wr_fifo.readable & ~self.wishbone.cyc, + self.wr_fifo.re.eq(1), + NextValue(adr, self.wr_fifo.dout[0:30]), + NextValue(data, self.wr_fifo.dout[30:62]), + NextValue(timeout, 127), + NextState("Write") + ) + ) + fsm.act("Write", + SBUS_DATA_OE_LED_2_o.eq(1), + self.wishbone.adr.eq(adr), + self.wishbone.dat_w.eq(data), + self.wishbone.we.eq(1), + self.wishbone.cyc.eq(1), + self.wishbone.stb.eq(1), + self.wishbone.sel.eq(2**len(self.wishbone.sel)-1), + NextValue(timeout, timeout - 1), + If(self.wishbone.ack, self.wishbone.we.eq(0), self.wishbone.cyc.eq(0), self.wishbone.stb.eq(0), NextState("Idle") - ) - fsm.act("Idle", - If(self.wr_fifo.readable & ~self.wishbone.cyc, - self.wr_fifo.re.eq(1), - NextValue(adr, self.wr_fifo.dout[0:30]), - NextValue(data, self.wr_fifo.dout[30:62]), - NextState("Write") - ), - If (rd_fifo_addr.readable & ~self.wishbone.cyc & self.rd_fifo_data.writable, - rd_fifo_addr.re.eq(1), - NextValue(adr, self.rd_fifo_addr.dout[0:30]), - NextState("Read") - ) - ) - fsm.act("Write", - self.wishbone.adr.eq(adr), - self.wishbone.dat_w.eq(data), - self.wishbone.we.eq(1), - self.wishbone.cyc.eq(1), - self.wishbone.stb.eq(1), - self.wishbone.sel.eq(2**len(self.wishbone.sel)-1), - If(self.wishbone.ack, - self.wishbone.we.eq(0), - self.wishbone.cyc.eq(0), - self.wishbone.stb.eq(0), - NextState("Idle") - ) + ).Elif(timeout == 0, # fixme, what to do to signal a problem ? + self.wishbone.we.eq(0), + self.wishbone.cyc.eq(0), + self.wishbone.stb.eq(0), + NextState("Idle") + ) ) fsm.act("Read", - self.wishbone.adr.eq(adr), + SBUS_DATA_OE_LED_2_o.eq(1), + self.wishbone.adr.eq(adr), + self.wishbone.we.eq(0), + self.wishbone.cyc.eq(1), + self.wishbone.stb.eq(1), + self.wishbone.sel.eq(2**len(self.wishbone.sel)-1), + NextValue(timeout, timeout - 1), + If(self.wishbone.ack, + self.rd_fifo_data.we.eq(1), + self.rd_fifo_data.din.eq(Cat(self.wishbone.dat_r, Signal(reset = 0))), self.wishbone.we.eq(0), - self.wishbone.cyc.eq(1), - self.wishbone.stb.eq(1), - self.wishbone.sel.eq(2**len(self.wishbone.sel)-1), - If(self.wishbone.ack, - self.rd_fifo_data.we.eq(1), - self.rd_fifo_data.din.eq(self.wishbone.dat_r), - self.wishbone.we.eq(0), - self.wishbone.cyc.eq(0), - self.wishbone.stb.eq(0), - NextState("Idle") - ) + self.wishbone.cyc.eq(0), + self.wishbone.stb.eq(0), + NextState("Idle") + ).Elif(timeout == 0, + self.rd_fifo_data.we.eq(1), + self.rd_fifo_data.din.eq(Cat(Signal(32, reset = 0xDEADBEEF), Signal(reset = 1))), + self.wishbone.we.eq(0), + self.wishbone.cyc.eq(0), + self.wishbone.stb.eq(0), + NextState("Idle") + ) )