-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathisa.py
More file actions
373 lines (360 loc) · 10.6 KB
/
isa.py
File metadata and controls
373 lines (360 loc) · 10.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
################################################
# RISC-V RV32I definitions and helper methods. #
################################################
from nmigen import *
# Instruction field definitions.
# RV32I opcode definitions:
OP_LUI = 0b0110111
OP_AUIPC = 0b0010111
OP_JAL = 0b1101111
OP_JALR = 0b1100111
OP_BRANCH = 0b1100011
OP_LOAD = 0b0000011
OP_STORE = 0b0100011
OP_REG = 0b0110011
OP_IMM = 0b0010011
OP_SYSTEM = 0b1110011
OP_FENCE = 0b0001111
# RV32I "funct3" bits. These select different functions with
# R-type, I-type, S-type, and B-type instructions.
F_JALR = 0b000
F_BEQ = 0b000
F_BNE = 0b001
F_BLT = 0b100
F_BGE = 0b101
F_BLTU = 0b110
F_BGEU = 0b111
F_LB = 0b000
F_LH = 0b001
F_LW = 0b010
F_LBU = 0b100
F_LHU = 0b101
F_SB = 0b000
F_SH = 0b001
F_SW = 0b010
F_ADDI = 0b000
F_SLTI = 0b010
F_SLTIU = 0b011
F_XORI = 0b100
F_ORI = 0b110
F_ANDI = 0b111
F_SLLI = 0b001
F_SRLI = 0b101
F_SRAI = 0b101
F_ADD = 0b000
F_SUB = 0b000
F_SLL = 0b001
F_SLT = 0b010
F_SLTU = 0b011
F_XOR = 0b100
F_SRL = 0b101
F_SRA = 0b101
F_OR = 0b110
F_AND = 0b111
# RV32I "funct7" bits. Along with the "funct3" bits, these select
# different functions with R-type instructions.
FF_SLLI = 0b0000000
FF_SRLI = 0b0000000
FF_SRAI = 0b0100000
FF_ADD = 0b0000000
FF_SUB = 0b0100000
FF_SLL = 0b0000000
FF_SLT = 0b0000000
FF_SLTU = 0b0000000
FF_XOR = 0b0000000
FF_SRL = 0b0000000
FF_SRA = 0b0100000
FF_OR = 0b0000000
FF_AND = 0b0000000
# CSR definitions, for 'ECALL' system instructions.
# Like with other "I-type" instructions, the 'funct3' bits select
# between different types of environment calls.
F_TRAPS = 0b000
F_CSRRW = 0b001
F_CSRRS = 0b010
F_CSRRC = 0b011
F_CSRRWI = 0b101
F_CSRRSI = 0b110
F_CSRRCI = 0b111
# Definitions for non-CSR 'ECALL' system instructions. These seem to
# use the whole 12-bit immediate to encode their functionality.
IMM_MRET = 0x302
IMM_WFI = 0x105
# ID numbers for different types of traps (exceptions).
TRAP_IMIS = 1
TRAP_ILLI = 2
TRAP_BREAK = 3
TRAP_LMIS = 4
TRAP_SMIS = 6
TRAP_ECALL = 11
# Flip a word of data.
def FLIP( v ):
return Cat( v[ 31 - i ] for i in range( 0, 32 ) )
# Convert a 32-bit word to little-endian byte format.
# 0x1234ABCD -> 0xCDAB3412
def LITTLE_END( v ):
return ( ( ( v & 0x000000FF ) << 24 ) |
( ( v & 0x0000FF00 ) << 8 ) |
( ( v & 0x00FF0000 ) >> 8 ) |
( ( v & 0xFF000000 ) >> 24 ) )
# Little-end conversion for use within an nMigen design.
def LITTLE_END_L( v ):
# Seems faster, but more LUTs.
return Cat( v[ 24 : 32 ], v[ 16 : 24 ], v[ 8 : 16 ], v[ 0 : 8 ] )
# Seems slower, but fewer LUTs.
#return LITTLE_END( v )
# Helper method to pretty-print a 2s-complement 32-bit hex string.
def hexs( h ):
if h >= 0:
return "0x%08X"%( h )
else:
return "0x%08X"%( ( h + ( 1 << 32 ) ) % ( 1 << 32 ) )
# ALU operation definitions. These implement the logic behind math
# instructions, e.g. 'ADD' covers 'ADD', 'ADDI', etc.
ALU_ADD = 0b0000
ALU_SUB = 0b1000
ALU_SLT = 0b0010
ALU_SLTU = 0b0011
ALU_XOR = 0b0100
ALU_OR = 0b0110
ALU_AND = 0b0111
ALU_SLL = 0b0001
ALU_SRL = 0b0101
ALU_SRA = 0b1101
# String mappings for opcodes, function bits, etc.
ALU_STRS = {
ALU_ADD: "+", ALU_SLT: "<", ALU_SLTU: "<",
ALU_XOR: "^", ALU_OR: "|", ALU_AND: "&",
ALU_SLL: "<<", ALU_SRL: ">>", ALU_SRA: ">>",
ALU_SUB: "-"
}
# CSR Addresses for the supported subset of 'Machine-Level ISA' CSRs.
# Machine information registers:
CSRA_MVENDORID = 0xF11
CSRA_MARCHID = 0xF12
CSRA_MIMPID = 0xF13
CSRA_MHARTID = 0xF14
# Machine trap setup:
CSRA_MSTATUS = 0x300
CSRA_MISA = 0x301
CSRA_MIE = 0x304
CSRA_MTVEC = 0x305
CSRA_MSTATUSH = 0x310
# Machine trap handling:
CSRA_MSCRATCH = 0x340
CSRA_MEPC = 0x341
CSRA_MCAUSE = 0x342
CSRA_MTVAL = 0x343
CSRA_MIP = 0x344
CSRA_MTINST = 0x34A
CSRA_MTVAL2 = 0x34B
# Machine counters:
CSRA_MCYCLE = 0xB00
CSRA_MINSTRET = 0xB02
# Machine counter setup:
CSRA_MCOUNTINHIBIT = 0x320
# CSR memory map definitions.
CSRS = {
'minstret': {
'c_addr': CSRA_MINSTRET,
'bits': { 'instrs': [ 0, 15, 'rw', 0 ] }
},
'mstatus': {
'c_addr': CSRA_MSTATUS,
'bits': {
'mie': [ 3, 3, 'rw', 0 ],
'mpie': [ 7, 7, 'r', 0 ]
}
},
'mcause': {
'c_addr': CSRA_MCAUSE,
'bits': {
'interrupt': [ 31, 31, 'rw', 0 ],
'ecode': [ 0, 30, 'rw', 0 ]
}
},
'mtval': {
'c_addr': CSRA_MTVAL,
'bits': { 'einfo': [ 0, 31, 'rw', 0 ] }
},
'mtvec': {
'c_addr': CSRA_MTVEC,
'bits': {
'mode': [ 0, 0, 'rw', 0 ],
'base': [ 2, 31, 'rw', 0 ]
}
},
'mepc': {
'c_addr': CSRA_MEPC,
'bits': {
'mepc': [ 2, 31, 'rw', 0 ]
}
},
}
# R-type operation: Rc = Ra ? Rb
# The '?' operation depends on the opcode, funct3, and funct7 bits.
def RV32I_R( op, f, ff, c, a, b ):
return LITTLE_END( ( op & 0x7F ) |
( ( c & 0x1F ) << 7 ) |
( ( f & 0x07 ) << 12 ) |
( ( a & 0x1F ) << 15 ) |
( ( b & 0x1F ) << 20 ) |
( ( ff & 0x7C ) << 25 ) )
# I-type operation: Rc = Ra ? Immediate
# The '?' operation depends on the opcode and funct3 bits.
def RV32I_I( op, f, c, a, i ):
return LITTLE_END( ( op & 0x7F ) |
( ( c & 0x1F ) << 7 ) |
( ( f & 0x07 ) << 12 ) |
( ( a & 0x1F ) << 15 ) |
( ( i & 0xFFF ) << 20 ) )
# S-type operation: Store Rb in Memory[ Ra + Immediate ]
# The funct3 bits select whether to store a byte, half-word, or word.
def RV32I_S( op, f, a, b, i ):
return LITTLE_END( ( op & 0x7F ) |
( ( i & 0x1F ) << 7 ) |
( ( f & 0x07 ) << 12 ) |
( ( a & 0x1F ) << 15 ) |
( ( b & 0x1F ) << 20 ) |
( ( ( i >> 5 ) & 0x7C ) ) )
# B-type operation: Branch to (PC + Immediate) if Ra ? Rb.
# The '?' compare operation depends on the funct3 bits.
# Note: the 12-bit immediate represents a 13-bit value with LSb = 0.
# This function accepts the 12-bit representation as an argument.
def RV32I_B( op, f, a, b, i ):
return LITTLE_END( ( op & 0x7F ) |
( ( ( i >> 10 ) & 0x01 ) << 7 ) |
( ( ( i ) & 0x0F ) << 8 ) |
( ( f & 0x07 ) << 12 ) |
( ( a & 0x1F ) << 15 ) |
( ( b & 0x1F ) << 20 ) |
( ( ( i >> 4 ) & 0x3F ) << 25 ) |
( ( ( i >> 11 ) & 0x01 ) << 31 ) )
# U-type operation: Load the 20-bit immediate into the most
# significant bits of Rc, setting the 12 least significant bits to 0.
# The opcode selects between LUI and AUIPC; AUIPC also adds the
# current PC address to the result which is stored in Rc.
def RV32I_U( op, c, i ):
return LITTLE_END( ( op & 0x7F ) |
( ( c & 0x1F ) << 7 ) |
( ( i & 0xFFFFF000 ) ) )
# J-type operation: In the base RV32I spec, this is only used by JAL.
# Jumps to (PC + Immediate) and stores (PC + 4) in Rc. The 20-bit
# immediate value represents a 21-bit value with LSb = 0; this
# function takes the 20-bit representation as an argument.
def RV32I_J( op, c, i ):
return LITTLE_END( ( op & 0x7F ) |
( ( c & 0x1F ) << 7 ) |
( ( ( i >> 11 ) & 0xFF ) << 12 ) |
( ( ( i >> 10 ) & 0x01 ) << 20 ) |
( ( ( i ) & 0x3FF ) << 21 ) |
( ( ( i >> 19 ) & 0x01 ) << 31 ) )
# Functions to assemble individual instructions.
# R-type operations:
def ADD( c, a, b ):
return RV32I_R( OP_REG, F_ADD, FF_ADD, c, a, b )
def SUB( c, a, b ):
return RV32I_R( OP_REG, F_SUB, FF_SUB, c, a, b )
def SLL( c, a, b ):
return RV32I_R( OP_REG, F_SLL, FF_SLL, c, a, b )
def SLT( c, a, b ):
return RV32I_R( OP_REG, F_SLT, FF_SLT, c, a, b )
def SLTU( c, a, b ):
return RV32I_R( OP_REG, F_SLTU, FF_SLTU, c, a, b )
def XOR( c, a, b ):
return RV32I_R( OP_REG, F_XOR, FF_XOR, c, a, b )
def SRL( c, a, b ):
return RV32I_R( OP_REG, F_SRL, FF_SRL, c, a, b )
def SRA( c, a, b ):
return RV32I_R( OP_REG, F_SRA, FF_SRA, c, a, b )
def OR( c, a, b ):
return RV32I_R( OP_REG, F_OR, FF_OR, c, a, b )
def AND( c, a, b ):
return RV32I_R( OP_REG, F_AND, FF_AND, c, a, b )
# Special case: immediate shift operations use
# 5-bit immediates, structured as an R-type operation.
def SLLI( c, a, i ):
return RV32I_R( OP_IMM, F_SLLI, FF_SLLI, c, a, i )
def SRLI( c, a, i ):
return RV32I_R( OP_IMM, F_SRLI, FF_SRLI, c, a, i )
def SRAI( c, a, i ):
return RV32I_R( OP_IMM, F_SRAI, FF_SRAI, c, a, i )
# I-type operations:
def JALR( c, a, i ):
return RV32I_I( OP_JALR, F_JALR, c, a, i )
def LB( c, a, i ):
return RV32I_I( OP_LOAD, F_LB, c, a, i )
def LH( c, a, i ):
return RV32I_I( OP_LOAD, F_LH, c, a, i )
def LW( c, a, i ):
return RV32I_I( OP_LOAD, F_LW, c, a, i )
def LBU( c, a, i ):
return RV32I_I( OP_LOAD, F_LBU, c, a, i )
def LHU( c, a, i ):
return RV32I_I( OP_LOAD, F_LHU, c, a, i )
def ADDI( c, a, i ):
return RV32I_I( OP_IMM, F_ADDI, c, a, i )
def SLTI( c, a, i ):
return RV32I_I( OP_IMM, F_SLTI, c, a, i )
def SLTIU( c, a, i ):
return RV32I_I( OP_IMM, F_SLTIU, c, a, i )
def XORI( c, a, i ):
return RV32I_I( OP_IMM, F_XORI, c, a, i )
def ORI( c, a, i ):
return RV32I_I( OP_IMM, F_ORI, c, a, i )
def ANDI( c, a, i ):
return RV32I_I( OP_IMM, F_ANDI, c, a, i )
# S-type operations:
def SB( a, b, i ):
return RV32I_S( OP_STORE, F_SB, a, b, i )
def SH( a, b, i ):
return RV32I_S( OP_STORE, F_SH, a, b, i )
def SW( a, b, i ):
return RV32I_S( OP_STORE, F_SW, a, b, i )
# B-type operations:
def BEQ( a, b, i ):
return RV32I_B( OP_BRANCH, F_BEQ, a, b, i )
def BNE( a, b, i ):
return RV32I_B( OP_BRANCH, F_BNE, a, b, i )
def BLT( a, b, i ):
return RV32I_B( OP_BRANCH, F_BLT, a, b, i )
def BGE( a, b, i ):
return RV32I_B( OP_BRANCH, F_BGE, a, b, i )
def BLTU( a, b, i ):
return RV32I_B( OP_BRANCH, F_BLTU, a, b, i )
def BGEU( a, b, i ):
return RV32I_B( OP_BRANCH, F_BGEU, a, b, i )
# U-type operations:
def LUI( c, i ):
return RV32I_U( OP_LUI, c, i )
def AUIPC( c, i ):
return RV32I_U( OP_AUIPC, c, i )
# J-type operation:
def JAL( c, i ):
return RV32I_J( OP_JAL, c, i )
# Assembly pseudo-ops:
def LI( c, i ):
if ( ( i & 0x0FFF ) & 0x0800 ):
return LUI( c, ( ( i >> 12 ) + 1 ) << 12 ), \
ADDI( c, c, ( i & 0x0FFF ) )
else:
return LUI( c, i ), ADDI( c, c, ( i & 0x0FFF ) )
def NOP():
return ADDI( 0, 0, 0x000 )
# Helper method to assemble a ROM image from a mix of instructions
# and assembly pseudo-operations.
def rom_img( arr ):
a = []
for i in arr:
if type( i ) == tuple:
for j in i:
a.append( j )
else:
a.append( i )
return a
# Helper method to assemble a RAM image for a test program.
def ram_img( arr ):
a = []
for i in arr:
a.append( i )
return a