Created
April 4, 2020 22:09
-
-
Save Ravenslofty/86ed2024f86e9c2d3b9ac4f1ed542adb to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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