Skip to content

Instantly share code, notes, and snippets.

@Ravenslofty
Created April 4, 2020 22:09
Show Gist options
  • Save Ravenslofty/86ed2024f86e9c2d3b9ac4f1ed542adb to your computer and use it in GitHub Desktop.
Save Ravenslofty/86ed2024f86e9c2d3b9ac4f1ed542adb to your computer and use it in GitHub Desktop.
from enum import Enum
from nmigen import Elaboratable, Repl, Signal, Module
from nmigen.back import rtlil, verilog
NORTH_MASK = 0xFFFFFFFFFFFFFF00
EAST_MASK = 0xFEFEFEFEFEFEFEFE
SOUTH_MASK = 0x00FFFFFFFFFFFFFF
WEST_MASK = 0x7F7F7F7F7F7F7F7F
NORTH_EAST_MASK = NORTH_MASK & EAST_MASK
SOUTH_EAST_MASK = SOUTH_MASK & EAST_MASK
SOUTH_WEST_MASK = SOUTH_MASK & WEST_MASK
NORTH_WEST_MASK = NORTH_MASK & WEST_MASK
class KnightAttacks(Elaboratable):
def __init__(self):
self.i = Signal(64, reset_less=True)
self.o = Signal(64, reset_less=True)
def elaborate(self, platform):
m = Module()
nne = Signal(64, reset_less=True)
nee = Signal(64, reset_less=True)
see = Signal(64, reset_less=True)
sse = Signal(64, reset_less=True)
ssw = Signal(64, reset_less=True)
sww = Signal(64, reset_less=True)
nww = Signal(64, reset_less=True)
nnw = Signal(64, reset_less=True)
m.d.comb += [
nne.eq((self.i << 17) & EAST_MASK),
nee.eq((self.i << 10) & 0xFCFCFCFCFCFCFCFC),
see.eq((self.i >> 6) & 0xFCFCFCFCFCFCFCFC),
sse.eq((self.i >> 15) & EAST_MASK),
ssw.eq((self.i >> 17) & WEST_MASK),
sww.eq((self.i >> 10) & 0x3F3F3F3F3F3F3F3F),
nww.eq((self.i << 6) & 0x3F3F3F3F3F3F3F3F),
nnw.eq((self.i << 15) & WEST_MASK),
self.o.eq(nne | nee | see | sse | ssw | sww | nww | nnw),
]
return m
class KingAttacks(Elaboratable):
def __init__(self):
self.i = Signal(64, reset_less=True)
self.o = Signal(64, reset_less=True)
def elaborate(self, platform):
m = Module()
n = Signal(64, reset_less=True)
e = Signal(64, reset_less=True)
s = Signal(64, reset_less=True)
w = Signal(64, reset_less=True)
m.d.comb += [
e.eq((self.i << 1) & EAST_MASK),
w.eq((self.i >> 1) & WEST_MASK),
n.eq(((e | self.i | w) << 8) & NORTH_MASK),
s.eq(((e | self.i | w) >> 8) & SOUTH_MASK),
self.o.eq(n | s),
]
return m
class KoggeStone(Elaboratable):
def __init__(self, shift, mask):
self.shift = shift
self.mask = mask
self.i_sliders = Signal(64, reset_less=True)
self.i_empty = Signal(64, reset_less=True)
self.o = Signal(64, reset_less=True)
def elaborate(self, platform):
m = Module()
pro0 = Signal(64, reset_less=True)
gen0 = Signal(64, reset_less=True)
pro1 = Signal(64, reset_less=True)
gen1 = Signal(64, reset_less=True)
pro2 = Signal(64, reset_less=True)
gen2 = Signal(64, reset_less=True)
m.d.comb += [
pro0.eq(self.i_empty & self.mask),
gen0.eq(
self.i_sliders | (pro0 & DirGolem._shift(self.i_sliders, self.shift))
),
pro1.eq(pro0 & DirGolem._shift(pro0, self.shift)),
gen1.eq(gen0 | (pro1 & DirGolem._shift(gen0, 2 * self.shift))),
pro2.eq(pro1 & DirGolem._shift(pro1, 2 * self.shift)),
gen2.eq(gen1 | (pro2 & DirGolem._shift(gen1, 4 * self.shift))),
self.o.eq(DirGolem._shift(gen2, self.shift) & self.mask),
]
return m
class DirGolem(Elaboratable):
def __init__(self):
self.i_black = Signal(64, reset_less=True)
self.i_pbq = Signal(64, reset_less=True)
self.i_nbk = Signal(64, reset_less=True)
self.i_rqk = Signal(64, reset_less=True)
self.o_north = Signal(64, reset_less=True)
self.o_east = Signal(64, reset_less=True)
self.o_south = Signal(64, reset_less=True)
self.o_west = Signal(64, reset_less=True)
self.o_norEa = Signal(64, reset_less=True)
self.o_souEa = Signal(64, reset_less=True)
self.o_souWe = Signal(64, reset_less=True)
self.o_norWe = Signal(64, reset_less=True)
self.o_noNoEa = Signal(64, reset_less=True)
self.o_noEaEa = Signal(64, reset_less=True)
self.o_soEaEa = Signal(64, reset_less=True)
self.o_soSoEa = Signal(64, reset_less=True)
self.o_soSoWe = Signal(64, reset_less=True)
self.o_soWeWe = Signal(64, reset_less=True)
self.o_noWeWe = Signal(64, reset_less=True)
self.o_noNoWe = Signal(64, reset_less=True)
@staticmethod
def _rotate_left(bb, shift):
return (bb << (shift & 63)) | (bb >> (63 ^ (shift & 63)))
@staticmethod
def _shift(bb, shift):
if shift > 0:
return bb << shift
return bb >> (-shift)
@staticmethod
def _more_than_one_set_lut4(m, bb):
# Optimised for LUT4; 4 logic levels, ceil(64/4) == 16 LUT4s
# Thanks to tnt on Freenode for this suggestion
level_1_any = Signal(16)
level_1_two = Signal(16)
for i in range(0, 16):
m.d.comb += level_1_any[i].eq(bb[4 * i : 4 * i + 4].bool())
with m.Switch(bb[4 * i : 4 * i + 4]):
with m.Case(0b0000):
m.d.comb += level_1_two[i].eq(0)
with m.Case(0b0001):
m.d.comb += level_1_two[i].eq(0)
with m.Case(0b0010):
m.d.comb += level_1_two[i].eq(0)
with m.Case(0b0100):
m.d.comb += level_1_two[i].eq(0)
with m.Case(0b1000):
m.d.comb += level_1_two[i].eq(0)
with m.Case():
m.d.comb += level_1_two[i].eq(1)
level_2_any = Signal(4)
level_2_two = Signal(8)
for i in range(0, 4):
m.d.comb += level_2_any[i].eq(level_1_any[4 * i : 4 * i + 4].bool())
with m.Switch(level_1_any[4 * i : 4 * i + 4]):
with m.Case(0b0000):
m.d.comb += level_2_two[i].eq(0)
with m.Case(0b0001):
m.d.comb += level_2_two[i].eq(0)
with m.Case(0b0010):
m.d.comb += level_2_two[i].eq(0)
with m.Case(0b0100):
m.d.comb += level_2_two[i].eq(0)
with m.Case(0b1000):
m.d.comb += level_2_two[i].eq(0)
with m.Case():
m.d.comb += level_2_two[i].eq(1)
m.d.comb += level_2_two[4 + i].eq(level_1_two[4 * i : 4 * i + 4].bool())
return level_2_two.bool() | (level_2_any == 0b1111)
@staticmethod
def _more_than_one_set_lut6(m, bb):
# Optimised for LUT6; 3 logic levels, ceil(64/6) == 11 LUT6s
# Thanks to tnt on Freenode for this suggestion
level_1_any = Signal(11)
level_1_two = Signal(11)
for i in range(0, 11):
m.d.comb += level_1_any[i].eq(bb[6 * i : 6 * i + 6].bool())
with m.Switch(bb[6 * i : 6 * i + 6]):
with m.Case(0b000000):
m.d.comb += level_1_two[i].eq(0)
with m.Case(0b000001):
m.d.comb += level_1_two[i].eq(0)
with m.Case(0b000010):
m.d.comb += level_1_two[i].eq(0)
with m.Case(0b000100):
m.d.comb += level_1_two[i].eq(0)
with m.Case(0b001000):
m.d.comb += level_1_two[i].eq(0)
with m.Case(0b010000):
m.d.comb += level_1_two[i].eq(0)
with m.Case(0b100000):
m.d.comb += level_1_two[i].eq(0)
with m.Case():
m.d.comb += level_1_two[i].eq(1)
level_2_any = Signal(2)
level_2_two = Signal(4)
for i in range(0, 2):
m.d.comb += level_2_any[i].eq(level_1_any[6 * i : 6 * i + 6].bool())
with m.Switch(level_1_any[6 * i : 6 * i + 6]):
with m.Case(0b000000):
m.d.comb += level_2_two[i].eq(0)
with m.Case(0b000001):
m.d.comb += level_2_two[i].eq(0)
with m.Case(0b000010):
m.d.comb += level_2_two[i].eq(0)
with m.Case(0b000100):
m.d.comb += level_2_two[i].eq(0)
with m.Case(0b001000):
m.d.comb += level_2_two[i].eq(0)
with m.Case(0b010000):
m.d.comb += level_2_two[i].eq(0)
with m.Case(0b100000):
m.d.comb += level_2_two[i].eq(0)
with m.Case():
m.d.comb += level_2_two[i].eq(1)
m.d.comb += level_2_two[2 + i].eq(level_1_two[6 * i : 6 * i + 6].bool())
return level_2_two.bool() | (level_2_any == 0b11)
@staticmethod
def _more_than_one_set(m, bb):
return DirGolem._more_than_one_set_lut4(m, bb)
# Dumb7Fill: lower area, higher delay
@staticmethod
def _dumb7fill(m, sliders, empty, shift, mask):
pro = Signal(64, reset_less=True)
gen0 = Signal(64, reset_less=True)
gen1 = Signal(64, reset_less=True)
gen2 = Signal(64, reset_less=True)
gen3 = Signal(64, reset_less=True)
gen4 = Signal(64, reset_less=True)
gen5 = Signal(64, reset_less=True)
gen6 = Signal(64, reset_less=True)
m.d.comb += [
pro.eq(empty & mask),
gen0.eq(sliders | (pro & DirGolem._rotate_left(sliders, shift))),
gen1.eq(gen0 | (pro & DirGolem._rotate_left(gen0, shift))),
gen2.eq(gen1 | (pro & DirGolem._rotate_left(gen1, shift))),
gen3.eq(gen2 | (pro & DirGolem._rotate_left(gen2, shift))),
gen4.eq(gen3 | (pro & DirGolem._rotate_left(gen3, shift))),
gen5.eq(gen4 | (pro & DirGolem._rotate_left(gen4, shift))),
gen6.eq(gen5 | (pro & DirGolem._rotate_left(gen5, shift))),
]
return DirGolem._rotate_left(gen6, shift) & mask
@staticmethod
def _knight_attacks(m, knights):
nne = Signal(64, reset_less=True)
nee = Signal(64, reset_less=True)
see = Signal(64, reset_less=True)
sse = Signal(64, reset_less=True)
ssw = Signal(64, reset_less=True)
sww = Signal(64, reset_less=True)
nww = Signal(64, reset_less=True)
nnw = Signal(64, reset_less=True)
m.d.comb += [
nne.eq((knights << 17) & EAST_MASK),
nee.eq((knights << 10) & 0xFCFCFCFCFCFCFCFC),
see.eq((knights >> 6) & 0xFCFCFCFCFCFCFCFC),
sse.eq((knights >> 15) & EAST_MASK),
ssw.eq((knights >> 17) & WEST_MASK),
sww.eq((knights >> 10) & 0x3F3F3F3F3F3F3F3F),
nww.eq((knights << 6) & 0x3F3F3F3F3F3F3F3F),
nnw.eq((knights << 15) & WEST_MASK),
]
return nne | nee | see | sse | ssw | sww | nww | nnw
def elaborate(self, platform):
m = Module()
m.submodules.king_slide_north = king_slide_north = KoggeStone(+8, NORTH_MASK)
m.submodules.king_slide_east = king_slide_east = KoggeStone(+1, EAST_MASK)
m.submodules.king_slide_south = king_slide_south = KoggeStone(-8, SOUTH_MASK)
m.submodules.king_slide_west = king_slide_west = KoggeStone(-1, WEST_MASK)
m.submodules.king_slide_norEa = king_slide_norEa = KoggeStone(
+9, NORTH_EAST_MASK
)
m.submodules.king_slide_souEa = king_slide_souEa = KoggeStone(
-7, SOUTH_EAST_MASK
)
m.submodules.king_slide_souWe = king_slide_souWe = KoggeStone(
-9, SOUTH_WEST_MASK
)
m.submodules.king_slide_norWe = king_slide_norWe = KoggeStone(
+7, NORTH_WEST_MASK
)
m.submodules.enemy_slide_north = enemy_slide_north = KoggeStone(+8, NORTH_MASK)
m.submodules.enemy_slide_east = enemy_slide_east = KoggeStone(+1, EAST_MASK)
m.submodules.enemy_slide_south = enemy_slide_south = KoggeStone(-8, SOUTH_MASK)
m.submodules.enemy_slide_west = enemy_slide_west = KoggeStone(-1, WEST_MASK)
m.submodules.enemy_slide_norEa = enemy_slide_norEa = KoggeStone(
+9, NORTH_EAST_MASK
)
m.submodules.enemy_slide_souEa = enemy_slide_souEa = KoggeStone(
-7, SOUTH_EAST_MASK
)
m.submodules.enemy_slide_souWe = enemy_slide_souWe = KoggeStone(
-9, SOUTH_WEST_MASK
)
m.submodules.enemy_slide_norWe = enemy_slide_norWe = KoggeStone(
+7, NORTH_WEST_MASK
)
m.submodules.slide_north = slide_north = KoggeStone(+8, NORTH_MASK)
m.submodules.slide_east = slide_east = KoggeStone(+1, EAST_MASK)
m.submodules.slide_south = slide_south = KoggeStone(-8, SOUTH_MASK)
m.submodules.slide_west = slide_west = KoggeStone(-1, WEST_MASK)
m.submodules.slide_norEa = slide_norEa = KoggeStone(+9, NORTH_EAST_MASK)
m.submodules.slide_souEa = slide_souEa = KoggeStone(-7, SOUTH_EAST_MASK)
m.submodules.slide_souWe = slide_souWe = KoggeStone(-9, SOUTH_WEST_MASK)
m.submodules.slide_norWe = slide_norWe = KoggeStone(+7, NORTH_WEST_MASK)
m.submodules.friendly_king_attacks = friendly_king_attacks = KingAttacks()
m.submodules.enemy_king_attacks = enemy_king_attacks = KingAttacks()
m.submodules.king_knight = king_knight = KnightAttacks()
m.submodules.enemy_knight_attacks = enemy_knight_attacks = KnightAttacks()
pawns = Signal(64, reset_less=True)
knights = Signal(64, reset_less=True)
bishops = Signal(64, reset_less=True)
rooks = Signal(64, reset_less=True)
queens = Signal(64, reset_less=True)
empty = Signal(64, reset_less=True)
friendly_pieces = Signal(64, reset_less=True)
friendly_king = Signal(64, reset_less=True)
enemy_king = Signal(64, reset_less=True)
king_xray = Signal(64, reset_less=True)
enemy_ortho = Signal(64, reset_less=True)
enemy_diag = Signal(64, reset_less=True)
enemy_pawn_souEa = Signal(64, reset_less=True)
enemy_pawn_souWe = Signal(64, reset_less=True)
king_pawn_norEa = Signal(64, reset_less=True)
king_pawn_norWe = Signal(64, reset_less=True)
king_attacks = Signal(64, reset_less=True)
enemy_attacks = Signal(64, reset_less=True)
horizontal_block_targets = Signal(64, reset_less=True)
vertical_block_targets = Signal(64, reset_less=True)
diagonal_block_targets = Signal(64, reset_less=True)
antidiag_block_targets = Signal(64, reset_less=True)
all_block_targets = Signal(64, reset_less=True)
blocks = Signal(64, reset_less=True)
null_if_check = Signal(64, reset_less=True)
null_if_double_check = Signal(64, reset_less=True)
check_from_orth = Signal(64, reset_less=True)
check_from_diag = Signal(64, reset_less=True)
check_from = Signal(64, reset_less=True)
check_to = Signal(64, reset_less=True)
target_mask = Signal(64, reset_less=True)
king_north = Signal(64, reset_less=True)
king_east = Signal(64, reset_less=True)
king_south = Signal(64, reset_less=True)
king_west = Signal(64, reset_less=True)
king_norEa = Signal(64, reset_less=True)
king_souEa = Signal(64, reset_less=True)
king_souWe = Signal(64, reset_less=True)
king_norWe = Signal(64, reset_less=True)
m.d.comb += [
# Useful constants
pawns.eq(self.i_pbq & ~self.i_nbk & ~self.i_rqk),
knights.eq(~self.i_pbq & self.i_nbk & ~self.i_rqk),
bishops.eq(self.i_pbq & self.i_nbk),
rooks.eq(~self.i_pbq & ~self.i_nbk & self.i_rqk),
queens.eq(self.i_pbq & self.i_rqk),
empty.eq(~self.i_pbq & ~self.i_nbk & ~self.i_rqk),
friendly_pieces.eq((self.i_pbq | self.i_nbk | self.i_rqk) & ~self.i_black),
friendly_king.eq(self.i_nbk & self.i_rqk & ~self.i_black),
enemy_king.eq(self.i_nbk & self.i_rqk & self.i_black),
# We need to X-ray through the king to avoid walking into attacked squares
king_xray.eq(empty | friendly_king),
# Enemy orthogonal pieces (for readability)
enemy_ortho.eq((rooks | queens) & self.i_black),
# Enemy diagonal pieces (for readability)
enemy_diag.eq((bishops | queens) & self.i_black),
# Enemy sliding pieces
enemy_slide_north.i_sliders.eq(enemy_ortho),
enemy_slide_north.i_empty.eq(king_xray),
enemy_slide_east.i_sliders.eq(enemy_ortho),
enemy_slide_east.i_empty.eq(king_xray),
enemy_slide_south.i_sliders.eq(enemy_ortho),
enemy_slide_south.i_empty.eq(king_xray),
enemy_slide_west.i_sliders.eq(enemy_ortho),
enemy_slide_west.i_empty.eq(king_xray),
enemy_slide_norEa.i_sliders.eq(enemy_diag),
enemy_slide_norEa.i_empty.eq(king_xray),
enemy_slide_souEa.i_sliders.eq(enemy_diag),
enemy_slide_souEa.i_empty.eq(king_xray),
enemy_slide_souWe.i_sliders.eq(enemy_diag),
enemy_slide_souWe.i_empty.eq(king_xray),
enemy_slide_norWe.i_sliders.eq(enemy_diag),
enemy_slide_norWe.i_empty.eq(king_xray),
# Enemy steppers
enemy_pawn_souEa.eq(((pawns & self.i_black) >> 7) & SOUTH_EAST_MASK),
enemy_pawn_souWe.eq(((pawns & self.i_black) >> 9) & SOUTH_WEST_MASK),
enemy_knight_attacks.i.eq(knights & self.i_black),
enemy_king_attacks.i.eq(enemy_king),
# Pretend the king is a superpiece
king_slide_north.i_sliders.eq(friendly_king),
king_slide_north.i_empty.eq(empty),
king_slide_east.i_sliders.eq(friendly_king),
king_slide_east.i_empty.eq(empty),
king_slide_south.i_sliders.eq(friendly_king),
king_slide_south.i_empty.eq(empty),
king_slide_west.i_sliders.eq(friendly_king),
king_slide_west.i_empty.eq(empty),
king_slide_norEa.i_sliders.eq(friendly_king),
king_slide_norEa.i_empty.eq(empty),
king_slide_souEa.i_sliders.eq(friendly_king),
king_slide_souEa.i_empty.eq(empty),
king_slide_souWe.i_sliders.eq(friendly_king),
king_slide_souWe.i_empty.eq(empty),
king_slide_norWe.i_sliders.eq(friendly_king),
king_slide_norWe.i_empty.eq(empty),
king_pawn_norEa.eq((friendly_king << 9) & NORTH_EAST_MASK),
king_pawn_norWe.eq((friendly_king << 7) & NORTH_WEST_MASK),
king_knight.i.eq(knights & friendly_pieces),
friendly_king_attacks.i.eq(friendly_king)
]
m.d.sync += [
king_attacks.eq(
king_slide_north.o
| king_slide_east.o
| king_slide_south.o
| king_slide_west.o
| king_slide_norEa.o
| king_slide_souEa.o
| king_slide_souWe.o
| king_slide_norWe.o
| king_pawn_norEa
| king_pawn_norWe
| king_knight.o
| friendly_king_attacks.o
),
# All squares the king can't touch.
enemy_attacks.eq(
enemy_slide_north.o
| enemy_slide_east.o
| enemy_slide_south.o
| enemy_slide_west.o
| enemy_slide_norEa.o
| enemy_slide_souEa.o
| enemy_slide_souWe.o
| enemy_slide_norWe.o
| enemy_pawn_souEa
| enemy_pawn_souWe
| enemy_knight_attacks.o
| enemy_king_attacks.o
),
# Block targets for single checks.
horizontal_block_targets.eq(
(enemy_slide_east.o & king_slide_west.o)
| (enemy_slide_west.o & king_slide_east.o)
),
vertical_block_targets.eq(
(enemy_slide_north.o & king_slide_south.o)
| (enemy_slide_south.o & king_slide_north.o)
),
diagonal_block_targets.eq(
(enemy_slide_norEa.o & king_slide_souWe.o)
| (enemy_slide_souWe.o & king_slide_norEa.o)
),
antidiag_block_targets.eq(
(enemy_slide_norWe.o & king_slide_souEa.o)
| (enemy_slide_souEa.o & king_slide_norWe.o)
),
check_from_orth.eq(
(
king_slide_north.o
| king_slide_east.o
| king_slide_south.o
| king_slide_west.o
)
& (rooks | queens)
& self.i_black
),
check_from_diag.eq(
(
king_slide_norEa.o
| king_slide_souEa.o
| king_slide_souWe.o
| king_slide_norWe.o
)
& (bishops | queens)
& self.i_black
),
check_from.eq(
check_from_orth
| check_from_diag
| (king_knight.o & knights & self.i_black)
| ((king_pawn_norEa | king_pawn_norWe) & pawns & self.i_black)
),
all_block_targets.eq(
horizontal_block_targets
| vertical_block_targets
| diagonal_block_targets
| antidiag_block_targets
),
blocks.eq(all_block_targets & empty),
null_if_check.eq(Repl((enemy_attacks & friendly_king).bool(), 64)),
null_if_double_check.eq(Repl(~self._more_than_one_set(m, check_from), 64)),
]
m.d.comb += [
check_to.eq(check_from | blocks | null_if_check),
target_mask.eq(~friendly_pieces & check_to & null_if_double_check),
# Finally, generate the moves:
# Sliders
slide_north.i_sliders.eq(
(rooks | queens)
& friendly_pieces
& ~(all_block_targets & ~vertical_block_targets)
),
slide_north.i_empty.eq(empty),
slide_south.i_sliders.eq(
(rooks | queens)
& friendly_pieces
& ~(all_block_targets & ~vertical_block_targets)
),
slide_south.i_empty.eq(empty),
slide_east.i_sliders.eq(
(rooks | queens)
& friendly_pieces
& ~(all_block_targets & ~horizontal_block_targets)
),
slide_east.i_empty.eq(empty),
slide_west.i_sliders.eq(
(rooks | queens)
& friendly_pieces
& ~(all_block_targets & ~horizontal_block_targets)
),
slide_west.i_empty.eq(empty),
slide_norEa.i_sliders.eq(
(bishops | queens)
& friendly_pieces
& ~(all_block_targets & ~diagonal_block_targets)
),
slide_norEa.i_empty.eq(empty),
slide_souWe.i_sliders.eq(
(bishops | queens)
& friendly_pieces
& ~(all_block_targets & ~diagonal_block_targets)
),
slide_souWe.i_empty.eq(empty),
slide_souEa.i_sliders.eq(
(bishops | queens)
& friendly_pieces
& ~(all_block_targets & ~antidiag_block_targets)
),
slide_souEa.i_empty.eq(empty),
slide_norWe.i_sliders.eq(
(bishops | queens)
& friendly_pieces
& ~(all_block_targets & ~antidiag_block_targets)
),
slide_norWe.i_empty.eq(empty),
# Kings
king_north.eq(
(friendly_king << 8) & NORTH_MASK & ~friendly_pieces & ~enemy_attacks
),
king_south.eq(
(friendly_king >> 8) & SOUTH_MASK & ~friendly_pieces & ~enemy_attacks
),
king_east.eq(
(friendly_king << 1) & EAST_MASK & ~friendly_pieces & ~enemy_attacks
),
king_west.eq(
(friendly_king >> 1) & WEST_MASK & ~friendly_pieces & ~enemy_attacks
),
king_norEa.eq(
(friendly_king << 9)
& NORTH_EAST_MASK
& ~friendly_pieces
& ~enemy_attacks
),
king_souEa.eq(
(friendly_king >> 7)
& SOUTH_EAST_MASK
& ~friendly_pieces
& ~enemy_attacks
),
king_souWe.eq(
(friendly_king >> 9)
& SOUTH_WEST_MASK
& ~friendly_pieces
& ~enemy_attacks
),
king_norWe.eq(
(friendly_king << 7)
& NORTH_WEST_MASK
& ~friendly_pieces
& ~enemy_attacks
),
]
m.d.sync += [
self.o_north.eq((slide_north.o | king_north) & target_mask),
self.o_east.eq((slide_east.o | king_east) & target_mask),
self.o_south.eq((slide_south.o | king_south) & target_mask),
self.o_west.eq((slide_west.o | king_west) & target_mask),
self.o_norEa.eq((slide_norEa.o | king_norEa) & target_mask),
self.o_souEa.eq((slide_souEa.o | king_souEa) & target_mask),
self.o_souWe.eq((slide_souWe.o | king_souWe) & target_mask),
self.o_norWe.eq((slide_norWe.o | king_norWe) & target_mask),
self.o_noNoEa.eq((knights << 17) & EAST_MASK & target_mask),
self.o_noEaEa.eq((knights << 10) & 0xFCFCFCFCFCFCFCFC & target_mask),
self.o_soEaEa.eq((knights >> 6) & 0xFCFCFCFCFCFCFCFC & target_mask),
self.o_soSoEa.eq((knights >> 15) & EAST_MASK & target_mask),
self.o_soSoWe.eq((knights >> 17) & WEST_MASK & target_mask),
self.o_soWeWe.eq((knights >> 10) & 0x3F3F3F3F3F3F3F3F & target_mask),
self.o_noWeWe.eq((knights << 6) & 0x3F3F3F3F3F3F3F3F & target_mask),
self.o_noNoWe.eq((knights << 15) & WEST_MASK & target_mask),
]
return m
class Register(Enum):
BLACK = 0
PBQ = 1
NBK = 2
RQK = 3
NORTH = 4
EAST = 5
SOUTH = 6
WEST = 7
NOREA = 8
SOUEA = 9
SOUWE = 10
NORWE = 11
NNEA = 12
NEEA = 13
SEEA = 14
SSEA = 15
SSWE = 16
SWWE = 17
NWWE = 18
NNWE = 19
class Registers(Elaboratable):
def __init__(self):
self.i_addr = Signal(5, reset_less=True)
self.i_data = Signal(64, reset_less=True)
self.o_data = Signal(64, reset_less=True)
self.r_black = Signal(64, reset_less=True)
self.r_pbq = Signal(64, reset_less=True)
self.r_nbk = Signal(64, reset_less=True)
self.r_rqk = Signal(64, reset_less=True)
self.r_north = Signal(64, reset_less=True)
self.r_east = Signal(64, reset_less=True)
self.r_south = Signal(64, reset_less=True)
self.r_west = Signal(64, reset_less=True)
self.r_norEa = Signal(64, reset_less=True)
self.r_souEa = Signal(64, reset_less=True)
self.r_souWe = Signal(64, reset_less=True)
self.r_norWe = Signal(64, reset_less=True)
self.r_noNoEa = Signal(64, reset_less=True)
self.r_noEaEa = Signal(64, reset_less=True)
self.r_soEaEa = Signal(64, reset_less=True)
self.r_soSoEa = Signal(64, reset_less=True)
self.r_soSoWe = Signal(64, reset_less=True)
self.r_soWeWe = Signal(64, reset_less=True)
self.r_noWeWe = Signal(64, reset_less=True)
self.r_noNoWe = Signal(64, reset_less=True)
def elaborate(self, platform):
m = Module()
m.submodules.dirgolem = dirgolem = DirGolem()
m.d.comb += [
dirgolem.i_black.eq(self.r_black),
dirgolem.i_pbq.eq(self.r_pbq),
dirgolem.i_nbk.eq(self.r_nbk),
dirgolem.i_rqk.eq(self.r_rqk),
]
with m.FSM():
with m.State("IO"):
with m.Switch(self.i_addr):
with m.Case(Register.BLACK):
m.d.sync += self.r_black.eq(self.i_data)
m.next = "RECALC"
with m.Case(Register.PBQ):
m.d.sync += self.r_pbq.eq(self.i_data)
m.next = "RECALC"
with m.Case(Register.NBK):
m.d.sync += self.r_nbk.eq(self.i_data)
m.next = "RECALC"
with m.Case(Register.RQK):
m.d.sync += self.r_rqk.eq(self.i_data)
m.next = "RECALC"
with m.Case(Register.NORTH):
m.d.sync += self.o_data.eq(self.r_north)
with m.Case(Register.EAST):
m.d.sync += self.o_data.eq(self.r_east)
with m.Case(Register.SOUTH):
m.d.sync += self.o_data.eq(self.r_south)
with m.Case(Register.WEST):
m.d.sync += self.o_data.eq(self.r_west)
with m.Case(Register.NOREA):
m.d.sync += self.o_data.eq(self.r_norEa)
with m.Case(Register.SOUEA):
m.d.sync += self.o_data.eq(self.r_souEa)
with m.Case(Register.SOUWE):
m.d.sync += self.o_data.eq(self.r_souWe)
with m.Case(Register.NORWE):
m.d.sync += self.o_data.eq(self.r_norWe)
with m.Case(Register.NNEA):
m.d.sync += self.o_data.eq(self.r_noNoEa)
with m.Case(Register.NEEA):
m.d.sync += self.o_data.eq(self.r_noEaEa)
with m.Case(Register.SEEA):
m.d.sync += self.o_data.eq(self.r_soEaEa)
with m.Case(Register.SSEA):
m.d.sync += self.o_data.eq(self.r_soSoEa)
with m.Case(Register.SSWE):
m.d.sync += self.o_data.eq(self.r_soSoWe)
with m.Case(Register.SWWE):
m.d.sync += self.o_data.eq(self.r_soWeWe)
with m.Case(Register.NWWE):
m.d.sync += self.o_data.eq(self.r_noWeWe)
with m.Case(Register.NNWE):
m.d.sync += self.o_data.eq(self.r_noNoWe)
with m.State("RECALC"):
m.d.sync += [
self.r_north.eq(dirgolem.o_north),
self.r_east.eq(dirgolem.o_east),
self.r_south.eq(dirgolem.o_south),
self.r_west.eq(dirgolem.o_west),
self.r_norEa.eq(dirgolem.o_norEa),
self.r_souEa.eq(dirgolem.o_souEa),
self.r_souWe.eq(dirgolem.o_souWe),
self.r_norWe.eq(dirgolem.o_norWe),
self.r_noNoEa.eq(dirgolem.o_noNoEa),
self.r_noEaEa.eq(dirgolem.o_noEaEa),
self.r_soEaEa.eq(dirgolem.o_soEaEa),
self.r_soSoEa.eq(dirgolem.o_soSoEa),
self.r_soSoWe.eq(dirgolem.o_soSoWe),
self.r_soWeWe.eq(dirgolem.o_soWeWe),
self.r_noWeWe.eq(dirgolem.o_noWeWe),
self.r_noNoWe.eq(dirgolem.o_noNoWe),
]
m.next = "IO"
return m
r = Registers()
ports = [r.i_addr, r.i_data, r.o_data]
print(verilog.convert(r, strip_internal_attrs=True, ports=ports))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment