You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

318 lines
12 KiB
Python

from enum import IntEnum, unique
from nmigen import Signal, Elaboratable, Module, ResetInserter, Cat
from nmigen_soc.wishbone import Interface as WishboneInterface
from nmigen.lib.fifo import SyncFIFOBuffered
from nmigen.back import verilog
@unique
class RegEnum(IntEnum):
BT_CTRL = 0
BMC2HOST_HOST2BMC = 1
BT_INTMASK = 2
@unique
class BMCRegEnum(IntEnum):
IRQ_MASK = 0
IRQ_STATUS = 1
BT_CTRL = 4
BMC2HOST_HOST2BMC = 5
@unique
class StateEnum(IntEnum):
IDLE = 0
ACK = 1
@unique
class BMCIRQEnum(IntEnum):
TARGET_TO_BMC_ATTN = 0
TARGET_NOT_BUSY = 1
class IPMI_BT(Elaboratable):
def __init__(self, depth=64):
self.depth = depth
self.bmc_wb = WishboneInterface(data_width=32, addr_width=3, granularity=8)
self.bmc_irq = Signal()
self.target_wb = WishboneInterface(data_width=8, addr_width=2)
self.target_irq = Signal()
def elaborate(self, platform):
m = Module()
# Reset signals for the FIFOs, since BT_CTRL needs to be able to clear them
reset_from_bmc_fifo = Signal()
reset_from_target_fifo = Signal()
m.d.sync += [
reset_from_bmc_fifo.eq(0),
reset_from_target_fifo.eq(0)
]
# BMC -> Target FIFO
m.submodules.from_bmc_fifo = from_bmc_fifo = ResetInserter(
reset_from_bmc_fifo)(SyncFIFOBuffered(width=8, depth=self.depth))
# Target -> BMC FIFO
m.submodules.from_target_fifo = from_target_fifo = ResetInserter(
reset_from_target_fifo)(SyncFIFOBuffered(width=8, depth=self.depth))
# Wire up wishbone to FIFO write
m.d.comb += [
from_bmc_fifo.w_data.eq(self.bmc_wb.dat_w),
from_target_fifo.w_data.eq(self.target_wb.dat_w)
]
# Some wishbone helpers
is_bmc_write = Signal()
is_bmc_read = Signal()
m.d.comb += [
is_bmc_write.eq(self.bmc_wb.stb & self.bmc_wb.cyc & self.bmc_wb.we),
is_bmc_read.eq(self.bmc_wb.stb & self.bmc_wb.cyc & ~self.bmc_wb.we)
]
is_target_read = Signal()
is_target_write = Signal()
m.d.comb += [
is_target_write.eq(self.target_wb.stb & self.target_wb.cyc & self.target_wb.we),
is_target_read.eq(self.target_wb.stb & self.target_wb.cyc & ~self.target_wb.we)
]
# BMC and target wishbone state machine
bmc_state = Signal(StateEnum, reset=StateEnum.IDLE)
target_state = Signal(StateEnum, reset=StateEnum.IDLE)
m.d.sync += [
from_bmc_fifo.w_en.eq(0),
from_bmc_fifo.r_en.eq(0),
from_target_fifo.w_en.eq(0),
from_target_fifo.r_en.eq(0)
]
m.d.sync += [
self.bmc_wb.ack.eq(0),
self.target_wb.ack.eq(0)
]
# Don't read from empty FIFOs
from_bmc_fifo_read_data = Signal(8)
m.d.comb += from_bmc_fifo_read_data.eq(0)
with m.If(from_bmc_fifo.r_rdy):
m.d.comb += from_bmc_fifo_read_data.eq(from_bmc_fifo.r_data)
from_target_fifo_read_data = Signal(8)
m.d.comb += from_target_fifo_read_data.eq(0)
with m.If(from_target_fifo.r_rdy):
m.d.comb += from_target_fifo_read_data.eq(from_target_fifo.r_data)
# BT_CTRL bits
target_to_bmc_attn = Signal()
bmc_to_target_attn = Signal()
sms_attn = Signal()
platform_reserved = Signal()
bmc_busy = Signal()
target_busy = Signal()
bt_ctrl = Signal(8)
m.d.comb += bt_ctrl.eq(Cat(0, 0, target_to_bmc_attn, bmc_to_target_attn,
sms_attn, platform_reserved, target_busy,
bmc_busy))
# BT_INTMASK (target interrupt mask) bits
bmc_to_target_irq_en = Signal()
bmc_to_target_irq = Signal()
m.d.comb += self.target_irq.eq(bmc_to_target_irq_en & bmc_to_target_irq)
# BMC interrupt bits. These are not architected by the IPMI BT spec. The
# Linux driver expects to get an interrupt whenever target_to_bmc_attn
# goes high (ready to read) or target_busy goes low (ready to write). We
# don't interrupt on bmc_to_target_attn going low (which is also required
# for ready to write) but rely on the target driver setting target_busy low
# right after it sets bmc_to_target_attn low.
bmc_irq_en = Signal(2)
bmc_irq = Signal(2)
m.d.comb += self.bmc_irq.eq(bmc_irq_en & bmc_irq)
# Target wishbone state machine
with m.Switch(target_state):
with m.Case(StateEnum.IDLE):
with m.If(is_target_write):
with m.Switch(self.target_wb.adr):
with m.Case(RegEnum.BT_CTRL):
# Bit 0, write 1 to clear the write fifo (ie from_target_fifo)
with m.If(self.target_wb.dat_w[0]):
m.d.sync += reset_from_target_fifo.eq(1)
# Bit 1 is meant to set the read FIFO to the next valid
# position, but since we only have a single buffer this
# doesn't need to do anything.
with m.If(self.target_wb.dat_w[2]):
# Trigger an interrupt whenever we set target_to_bmc_attn
with m.If(bmc_irq_en[BMCIRQEnum.TARGET_TO_BMC_ATTN]):
m.d.sync += bmc_irq[BMCIRQEnum.TARGET_TO_BMC_ATTN].eq(1)
m.d.sync += target_to_bmc_attn.eq(1)
# Bit 3, write 1 to clear bmc_to_target_attn
with m.If(self.target_wb.dat_w[3]):
m.d.sync += bmc_to_target_attn.eq(0)
# Bit 4, write 1 to clear sms_attn
with m.If(self.target_wb.dat_w[4]):
m.d.sync += sms_attn.eq(0)
# Bit 5, write 1 to set platform reserved
with m.If(self.target_wb.dat_w[5]):
m.d.sync += platform_reserved.eq(1)
# Bit 6, write 1 to toggle target_busy
with m.If(self.target_wb.dat_w[6]):
# Trigger an interrupt whenever we clear target_busy
with m.If(target_busy & bmc_irq_en[BMCIRQEnum.TARGET_NOT_BUSY]):
m.d.sync += bmc_irq[BMCIRQEnum.TARGET_NOT_BUSY].eq(1)
m.d.sync += target_busy.eq(~target_busy)
# Bit 7, read only
with m.Case(RegEnum.BMC2HOST_HOST2BMC):
# Only assert write if there is space
m.d.sync += from_target_fifo.w_en.eq(from_target_fifo.w_rdy)
with m.Case(RegEnum.BT_INTMASK):
# Bit 0, 0/1 write
m.d.sync += bmc_to_target_irq_en.eq(self.target_wb.dat_w[0])
# Bit 1, write 1 to clear interrupt
with m.If(self.target_wb.dat_w[1]):
m.d.sync += bmc_to_target_irq.eq(0)
m.d.sync += [
self.target_wb.ack.eq(1),
target_state.eq(StateEnum.ACK)
]
with m.If(is_target_read):
with m.Switch(self.target_wb.adr):
with m.Case(RegEnum.BT_CTRL):
m.d.sync += self.target_wb.dat_r.eq(bt_ctrl)
with m.Case(RegEnum.BMC2HOST_HOST2BMC):
m.d.sync += [
self.target_wb.dat_r.eq(from_bmc_fifo_read_data),
from_bmc_fifo.r_en.eq(from_bmc_fifo.r_rdy),
]
with m.Case(RegEnum.BT_INTMASK):
m.d.sync += self.target_wb.dat_r.eq(Cat(bmc_to_target_irq_en, bmc_to_target_irq))
m.d.sync += [
self.target_wb.ack.eq(1),
target_state.eq(StateEnum.ACK)
]
with m.Case(StateEnum.ACK):
m.d.sync += [
self.target_wb.ack.eq(0),
target_state.eq(StateEnum.IDLE),
]
# BMC wishbone state machine
with m.Switch(bmc_state):
with m.Case(StateEnum.IDLE):
with m.If(is_bmc_write):
with m.Switch(self.bmc_wb.adr):
with m.Case(BMCRegEnum.BT_CTRL):
# Bit 0, write 1 to clear the write fifo (ie from_bmc_fifo)
with m.If(self.bmc_wb.dat_w[0]):
m.d.sync += reset_from_bmc_fifo.eq(1)
# Bit 1 is meant to set the read FIFO to the next valid
# position, but since we only have a single buffer this
# doesn't need to do anything.
# Bit 2, write to clear target_to_bmc_attn
with m.If(self.bmc_wb.dat_w[2]):
m.d.sync += target_to_bmc_attn.eq(0)
# Bit 3, write 1 to set bmc_to_target_attn
with m.If(self.bmc_wb.dat_w[3]):
# Trigger an interrupt whenever we set bmc_to_target_attn
with m.If(bmc_to_target_irq_en):
m.d.sync += bmc_to_target_irq.eq(1)
m.d.sync += bmc_to_target_attn.eq(1)
# Bit 4, write 1 to set sms_attn
with m.If(self.bmc_wb.dat_w[4]):
# Trigger an interrupt whenever we set sms_attn
with m.If(bmc_to_target_irq_en):
m.d.sync += bmc_to_target_irq.eq(1)
m.d.sync += sms_attn.eq(1)
# Bit 5, write 1 to clear platform reserved
with m.If(self.bmc_wb.dat_w[5]):
m.d.sync += platform_reserved.eq(0)
# Bit 6, read only
# Bit 7, write 1 to toggle bmc_busy
with m.If(self.bmc_wb.dat_w[7]):
m.d.sync += bmc_busy.eq(~bmc_busy)
with m.Case(BMCRegEnum.BMC2HOST_HOST2BMC):
# Only assert write if there is space
m.d.sync += from_bmc_fifo.w_en.eq(from_bmc_fifo.w_rdy)
with m.Case(BMCRegEnum.IRQ_MASK):
m.d.sync += bmc_irq_en.eq(self.bmc_wb.dat_w)
with m.Case(BMCRegEnum.IRQ_STATUS):
m.d.sync += bmc_irq.eq(self.bmc_wb.dat_w)
m.d.sync += [
self.bmc_wb.ack.eq(1),
bmc_state.eq(StateEnum.ACK)
]
with m.If(is_bmc_read):
with m.Switch(self.bmc_wb.adr):
with m.Case(BMCRegEnum.BT_CTRL):
m.d.sync += self.bmc_wb.dat_r.eq(bt_ctrl)
with m.Case(BMCRegEnum.BMC2HOST_HOST2BMC):
m.d.sync += [
self.bmc_wb.dat_r.eq(from_target_fifo_read_data),
from_target_fifo.r_en.eq(from_target_fifo.r_rdy),
]
with m.Case(BMCRegEnum.IRQ_MASK):
m.d.sync += self.bmc_wb.dat_r.eq(bmc_irq_en)
with m.Case(BMCRegEnum.IRQ_STATUS):
m.d.sync += self.bmc_wb.dat_r.eq(bmc_irq)
m.d.sync += [
self.bmc_wb.ack.eq(1),
bmc_state.eq(StateEnum.ACK)
]
with m.Case(StateEnum.ACK):
m.d.sync += [
self.bmc_wb.ack.eq(0),
bmc_state.eq(StateEnum.IDLE),
]
return m
if __name__ == "__main__":
top = IPMI_BT()
with open("ipmi_bt.v", "w") as f:
f.write(verilog.convert(top))