Created
April 4, 2020 22:09
-
-
Save Ravenslofty/86ed2024f86e9c2d3b9ac4f1ed542adb to your computer and use it in GitHub Desktop.
This file contains hidden or 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