Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: whitequark/glasgow
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: ee057bf3da73
Choose a base ref
...
head repository: whitequark/glasgow
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 31fd5cea2d4e
Choose a head ref
  • 1 commit
  • 3 files changed
  • 1 contributor

Commits on Dec 3, 2018

  1. Copy the full SHA
    31fd5ce View commit details
Showing with 402 additions and 0 deletions.
  1. +244 −0 software/glasgow/arch/boneless/__init__.py
  2. +107 −0 software/glasgow/arch/boneless/instr.py
  3. +51 −0 software/glasgow/arch/boneless/opcode.py
244 changes: 244 additions & 0 deletions software/glasgow/arch/boneless/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
# Introduction
# ------------
#
# _Boneless_ is a CPU architecture created specifically for Glasgow. It is designed for applet
# control plane, with the goals being minimal FPGA resource (logic and timing) consumption while
# still remaining easily programmable in hand-written assembly. It is not directly derived from
# any major CPU architecture, but borrows ideas from cores such as 8051, MIPS and AVR.
#
# This file is the primary document defining Boneless. Changing this file constitutes changing
# the authoritative definition of the architecture. Boneless should only be used outside of
# Glasgow if it is forked and renamed.
#
# Overview
# --------
#
# The major characteristics of the Boneless architecture are:
# * Radical von Neumann architecture; registers, instructions and data share address space.
# * Registers defined as an aligned, movable window into common address space.
# * Unified 16-bit register, instruction and data word size; word-addressable only.
# * Unified PC-relative memory addressing for code and data offsets.
# * Five instruction classes:
# - A-class, for ALU operations.
# - S-class, for shift operations.
# - M-class, for load-store operations. 5-bit zero-extended offset.
# - I-class, for operations with immediates. 8-bit sign-extended immediate.
# - C-class, for control transfers. 11-bit sign-extended offset.
# * Four flags: Z (zero), S (sign), C (unsigned carry), O (signed carry).
# * Secondary address space for special-purpose registers.
#
# As a result, Boneless can be efficiently implemented with a single 16-bit wide single-port
# block RAM primitive, e.g. on iCE40UP5K, this could be one 16Kx16 SPRAM or one 256x16 BRAM.
#
# Instruction format
# ------------------
#
# Instruction classes are laid out as follows:
#
# +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
# | F | E | D | C | B | A | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
# +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
# A-class | 0 | 0 | 0 | 0 | c | R-dst | R-opa | R-opb | type |
# +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
# S-class | 0 | 0 | 0 | 1 | c | R-dst | R-opa | amount | t |
# +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
# M-class | 0 | 0 | 1 | code | R-src/dst | R-adr | offset |
# +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
# I-class | 0 | 1 | opcode | R-src/dst | immediate |
# +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
# C-class | 1 | opcode | F | offset |
# +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
#
# Instruction decoding
# --------------------
#
# The instruction opcodes are structured as a prefix code optimized for fast decoding. Decoding
# proceeds as follows for instructions starting with...
# * 0000x (A-class): load R-opb, load R-opa, and store R-dst;
# * 0001x (S-class): load R-opa, and store R-dst;
# * 001x0 (M-class LD): load R-adr, load memory, and store R-dst;
# * 001x1 (M-class ST): load R-adr, load R-src, and store memory;
# * 0100x (I-class r-m-w): load R-src/dst, and store R-src/dst.
# * 0101x (I-class w): store R-dst.
# * 01100 (I-class LD): load memory, and store R-dst.
# * 01101 (I-class ST): load R-src, and store memory.
# * 01110 (I-class JAL): store R-dst, and jump.
# * 01111 (I-class JR): load R-src, and jump.
# * 1xxxx (C-class): jump.
#
# As a result, Boneless instruction decoding can be implemented with approximately 10 4-LUTs.
#
# Instruction set summary
# -----------------------
#
# * class=A
# - code=0 (arithmetic)
# + type=00 ADD
# + type=01 SUB
# + type=10 CMP
# + type=11 (1 unassigned)
# - code=1 (logic)
# + type=00 AND
# + type=01 OR
# + type=10 XOR
# + type=11 (1 unassigned)
# * class=S
# - code=0
# + type=0 SLL, MOV
# + type=1 ROT
# - code=1
# + type=0 SRL
# + type=1 SRA
# * class=M
# - code=00 LD
# - code=01 ST
# - code=10 LDX
# - code=11 STX
# * class=I
# - code=000 ADDI/SUBI
# - code=001 MOVA
# - code=010 MOVL
# - code=011 MOVH
# - code=100 LDI
# - code=101 STI
# - code=110 JAL
# - code=111 JR
# * class=C
# - code=000
# + flag=0 J
# + flag=1 (1 unassigned)
# - code=001 JNZ/JNE, JZ/JE
# - code=010 JNS, JS
# - code=011 JNO, JO
# - code=100 JNC/JUGE, JC/JULT
# - code=101 JUGT, JULE
# - code=110 JSGE, JSLT
# - code=111 JSGT, JSLE
#
# Move instructions
# -----------------
#
# Mnemonic: MOV Rd, Ra
# Operation: Rd ← Ra
#
# Mnemonic: MOVL Rd, imm
# Operation: Rd[15:8] ← 0, Rd[7:0] ← imm
#
# Mnemonic: MOVH Rd, imm
# Operation: Rd[15:8] ← imm, Rd[7:0] ← 0
#
# Mnemonic: MOVI Rd, imm (pseudo)
# Operation: Rd ← imm
#
# Mnemonic: MOVA Rd, ±off
# Operation: Rd ← PC+1+off
#
# Arithmetic instructions
# -----------------------
#
# Mnemonic: ADD Rd, Ra, Rb
# SUB Rd, Ra, Rb
# Operation: Rd ← Ra · Rb
# ZSCO ← flags(Rd)
#
# Mnemonic: ADDI Rd, ±imm
# SUBI Rd, ±imm (pseudo)
# Operation: Rd ← Rd + imm
# ZSCO ← flags(Rd)
#
# Mnemonic: CMP Rb, Ra
# Operation: t ← Ra - Rb
# ZSCO ← flags(t)
#
# Logic instructions
# ------------------
#
# Mnemonic: AND Rd, Ra, Rb
# OR Rd, Ra, Rb
# XOR Rd, Ra, Rb
# Operation: Rd ← Ra · Rb
# ZSCO ← flags(Rd)
#
# Shift instructions
# ------------------
#
# Mnemonic: SLL Rd, Ra, amt
# Operation: Rd ← {Ra[15-amt:0], amt{0}}
# ZSCO ← flags(Rd)
#
# Mnemonic: ROT Rd, Ra, amt
# ROL Rd, Ra, amt (alias)
# ROR Rd, Ra, amt (pseudo)
# Operation: Rd ← {Ra[15-amt:0], Ra[15:15-amt]}
# ZSCO ← flags(Rd)
#
# Mnemonic: SRL Rd, Ra, amt
# Operation: Rd ← {15-amt{0}, Ra[15:amt]}
# ZSCO ← flags(Rd)
#
# Mnemonic: SRA Rd, Ra, amt
# Operation: Rd ← {15-amt{Ra[15]}, Ra[15:amt]}
# ZSCO ← flags(Rd)
#
# Memory instructions
# -------------------
#
# Mnemonic: LD Rd, Ra, off
# Operation: Rd ← mem[Ra+off]
#
# Mnemonic: LDI Rd, ±off
# Operation: Rd ← mem[PC+off]
#
# Mnemonic: LDX Rd, Ra, off
# Operation: Rd ← ext[Ra+off]
#
# Mnemonic: ST Rs, Ra, off
# Operation: mem[Ra+off] ← Rs
#
# Mnemonic: STI Rs, ±off
# Operation: mem[PC+off] ← Rs
#
# Mnemonic: STX Rs, Ra, off
# Operation: ext[Ra+off] ← Rs
#
# Control instructions
# --------------------
#
# Mnemonic: J ±off
# Operation: PC ← PC+1+off
#
# Mnemonic: JAL Rd, ±off
# Operation: Rd ← PC+1
# PC ← PC+1+off
#
# Mnemonic: JR Rs, ±off
# Operation: PC ← Rs+off
#
# Mnemonic: JNZ ±off (F=0)
# JZ ±off (F=1)
# JNE ±off (F=0)
# JE ±off (F=1)
# Operation: if(Z = F)
# PC ← PC+1+off
#
# Mnemonic: JNC ±off (F=0)
# JC ±off (F=1)
# JUGE ±off (F=0)
# JULT ±off (F=1)
# Operation: if(C = F)
# PC ← PC+1+off
#
# Mnemonic: JUGT ±off (F=0)
# JULE ±off (F=1)
# Operation: if((C or Z) = F)
# PC ← PC+1+off
#
# Mnemonic: JSGE ±off (F=0)
# JSLT ±off (F=1)
# Operation: if((S xor O) = F)
# PC ← PC+1+off
#
# Mnemonic: JSGT ±off (F=0)
# JSLE ±off (F=1)
# Operation: if(((S xor O) or Z) = F)
# PC ← PC+1+off
107 changes: 107 additions & 0 deletions software/glasgow/arch/boneless/instr.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
__all__ = [
"R0", "R1", "R2", "R3", "R4", "R5", "R6", "R7",
"ADD", "ADDI", "ADDU", "AND", "CMP", "J", "JAL", "JE", "JG", "JGE", "JL", "JLE", "JNE",
"JNS", "JNZ", "JR", "JS", "JZ", "LD", "LDI", "LDX", "MOV", "MOVA", "MOVH", "MOVI", "MOVL",
"OR", "ROT", "SLL", "SRA", "SRL", "ST", "STI", "STX", "SUB", "SUBI", "SUBU", "XOR",
]


def A_FORMAT(opcode, optype, rd, ra, rb):
assert rd in range(8) and ra in range(8) and rb in range(8)
return (((opcode & 0b11111) << 10) |
(( rd & 0b111) << 8) |
(( ra & 0b111) << 5) |
(( rb & 0b111) << 2) |
((optype & 0b11) << 0))

def S_FORMAT(opcode, optype, rd, ra, amt):
assert rd in range(8) and ra in range(8) and amt in range(16)
return (((opcode & 0b11111) << 10) |
(( rd & 0b111) << 8) |
(( ra & 0b111) << 5) |
(( amt & 0b1111) << 1) |
((optype & 0b1) << 0))

def M_FORMAT(opcode, rsd, ra, off):
assert rsd in range(8) and ra in range(8) and -16 <= off <= 15
return (((opcode & 0b11111) << 10) |
(( rsd & 0b111) << 8) |
(( ra & 0b111) << 5) |
(( off & 0b11111) << 0))

def I_FORMAT(opcode, rsd, imm, u=False):
assert rsd in range(8) and ((not u and -128 <= imm <= 127) or
(u and imm in range(256)))
return (((opcode & 0b11111) << 10) |
(( rsd & 0b111) << 8) |
(( imm & 0xff) << 0))

def C_FORMAT(opcode, off):
assert -1024 <= off <= 1023
return (((opcode & 0b11111) << 10) |
(( off & 0x7ff) << 0))


R0, R1, R2, R3, R4, R5, R6, R7 = range(8)


def ADD (rd, ra, rb): return [A_FORMAT(OPCODE_ARITH, OPTYPE_ADD, rd, ra, rb)]
def SUB (rd, ra, rb): return [A_FORMAT(OPCODE_ARITH, OPTYPE_SUB, rd, ra, rb)]
def CMP (rd, ra, rb): return [A_FORMAT(OPCODE_ARITH, OPTYPE_CMP, rd, ra, rb)]

def AND (rd, ra, rb): return [A_FORMAT(OPCODE_LOGIC, OPTYPE_AND, rd, ra, rb)]
def OR (rd, ra, rb): return [A_FORMAT(OPCODE_LOGIC, OPTYPE_OR, rd, ra, rb)]
def XOR (rd, ra, rb): return [A_FORMAT(OPCODE_LOGIC, OPTYPE_XOR, rd, ra, rb)]

def SLL (rd, ra, amt): return [S_FORMAT(OPCODE_SHIFT_L, OPTYPE_SLL, rd, ra, amt)]
def ROT (rd, ra, amt): return [S_FORMAT(OPCODE_SHIFT_L, OPTYPE_ROT, rd, ra, amt)]
def SRL (rd, ra, amt): return [S_FORMAT(OPCODE_SHIFT_R, OPTYPE_SRL, rd, ra, amt)]
def SRA (rd, ra, amt): return [S_FORMAT(OPCODE_SHIFT_R, OPTYPE_SRA, rd, ra, amt)]
def MOV (rd, rs): return [S_FORMAT(OPCODE_SHIFT_L, OPTYPE_SLL, rd, ra, 0)]

def LD (rd, ra, off): return [M_FORMAT(OPCODE_LD, rd, ra, off)]
def ST (rs, ra, off): return [M_FORMAT(OPCODE_ST, rs, ra, off)]
def LDX (rd, ra, off): return [M_FORMAT(OPCODE_LDX, rd, ra, off)]
def STX (rs, ra, off): return [M_FORMAT(OPCODE_STX, rs, ra, off)]

def ADDI(rd, imm): return [I_FORMAT(OPCODE_ADDI, rd, +imm)]
def SUBI(rd, imm): return [I_FORMAT(OPCODE_ADDI, rd, -imm)]

def MOVL(rd, imm): return [I_FORMAT(OPCODE_MOVL, rd, imm, u=True)]
def MOVH(rd, imm): return [I_FORMAT(OPCODE_MOVH, rd, imm, u=True)]

def MOVA(rd, off): return [I_FORMAT(OPCODE_MOVA, rd, off)]
def LDI (rd, off): return [I_FORMAT(OPCODE_LDI, rd, off)]
def STI (rs, off): return [I_FORMAT(OPCODE_STI, rs, off)]
def JAL (rd, off): return [I_FORMAT(OPCODE_JAL, rd, off)]
def JR (rd, off): return [I_FORMAT(OPCODE_JR, rd, off)]

def J (off): return [C_FORMAT(OPCODE_J, off)]

def JNZ (off): return [C_FORMAT(OPCODE_JNZ, off)]
def JZ (off): return [C_FORMAT(OPCODE_JZ, off)]
def JNS (off): return [C_FORMAT(OPCODE_JNS, off)]
def JS (off): return [C_FORMAT(OPCODE_JS, off)]
def JNC (off): return [C_FORMAT(OPCODE_JNC, off)]
def JC (off): return [C_FORMAT(OPCODE_JC, off)]
def JNO (off): return [C_FORMAT(OPCODE_JNO, off)]
def JO (off): return [C_FORMAT(OPCODE_JO, off)]

def JNE (off): return [C_FORMAT(OPCODE_JNE, off)]
def JE (off): return [C_FORMAT(OPCODE_JE, off)]
def JUGE(off): return [C_FORMAT(OPCODE_JUGE, off)]
def JULT(off): return [C_FORMAT(OPCODE_JULT, off)]
def JUGT(off): return [C_FORMAT(OPCODE_JUGT, off)]
def JULE(off): return [C_FORMAT(OPCODE_JULE, off)]
def JSGE(off): return [C_FORMAT(OPCODE_JSGE, off)]
def JSLT(off): return [C_FORMAT(OPCODE_JSLT, off)]
def JSGT(off): return [C_FORMAT(OPCODE_JSGT, off)]
def JSLE(off): return [C_FORMAT(OPCODE_JSLE, off)]

def MOVI(rd, imm16):
assert imm16 in range(65536)
if imm16 in range(256):
return MOVL(rd, imm16)
else:
return MOVH(rd, (imm16 >> 8) + ((imm16 >> 7) & 1)) + \
I_FORMAT(OPCODE_ADDI, rd, imm16 & 0xff, u=True)
51 changes: 51 additions & 0 deletions software/glasgow/arch/boneless/opcode.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
OPCODE_ARITH = 0b0000_0
OPTYPE_ADD = 0b00
OPTYPE_SUB = 0b01
OPTYPE_CMP = 0b10

OPCODE_LOGIC = 0b0000_1
OPTYPE_AND = 0b00
OPTYPE_OR = 0b01
OPTYPE_XOR = 0b10

OPCODE_SHIFT_L = 0b0001_0
OPTYPE_SLL = 0b0
OPTYPE_ROT = 0b1

OPCODE_SHIFT_R = 0b0001_1
OPTYPE_SRL = 0b0
OPTYPE_SRR = 0b1

OPCODE_LD = 0b001_00
OPCODE_ST = 0b001_01
OPCODE_LDX = 0b001_10
OPCODE_STX = 0b001_11

OPCODE_ADDI = 0b01_000
OPCODE_MOVA = 0b01_001
OPCODE_MOVL = 0b01_010
OPCODE_MOVH = 0b01_011
OPCODE_LDI = 0b01_100
OPCODE_STI = 0b01_101
OPCODE_JAL = 0b01_110
OPCODE_JR = 0b01_111

OPCODE_J = 0b1_000_0
OPCODE_JNZ = 0b1_001_0
OPCODE_JNE = OPCODE_JNZ
OPCODE_JZ = 0b1_001_1
OPCODE_JE = OPCODE_JZ
OPCODE_JNS = 0b1_010_0
OPCODE_JS = 0b1_010_1
OPCODE_JNO = 0b1_011_0
OPCODE_JO = 0b1_011_1
OPCODE_JNC = 0b1_100_0
OPCODE_JUGE = OPCODE_JNC
OPCODE_JC = 0b1_100_1
OPCODE_JULT = OPCODE_JC
OPCODE_JUGT = 0b1_101_0
OPCODE_JULE = 0b1_101_1
OPCODE_JSGE = 0b1_110_0
OPCODE_JSLT = 0b1_110_1
OPCODE_JSGT = 0b1_111_0
OPCODE_JSLE = 0b1_111_1