:add lab7 spoc exercise related codes
This commit is contained in:
parent
69afb3405b
commit
f9b1bb3b66
227
related_info/lab7/lab7-spoc-exercise.md
Normal file
227
related_info/lab7/lab7-spoc-exercise.md
Normal file
@ -0,0 +1,227 @@
|
||||
# lab7 理解race condition
|
||||
## x86模拟运行环境
|
||||
x86.py是一个模拟执行基于汇编代码的多线程执行过程的模拟器。这里的汇编语法是基于很简单的x86汇编语法。
|
||||
且没有包括OS的调度、context切换和中断处理过程。每条指令大小为一个byte。每个变量占4个byte
|
||||
在硬件上模拟了4个通用寄存器:
|
||||
```
|
||||
%ax, %bx, %cx, %dx
|
||||
```
|
||||
一个程序计数器`pc`,一个堆栈寄存器`sp`,和一小部分指令:
|
||||
```
|
||||
mov immediate, register # immediate value --> register
|
||||
mov memory, register # memory --> register
|
||||
mov register, register # register --> register
|
||||
mov register, memory # register --> memory
|
||||
mov immediate, memory # immediate value --> memory
|
||||
|
||||
add immediate, register # register = register + immediate
|
||||
add register1, register2 # register2 = register2 + register1
|
||||
sub immediate, register # register = register - immediate
|
||||
sub register1, register2 # register2 = register2 - register1
|
||||
|
||||
test immediate, register # compare immediate and register (set condition codes)
|
||||
test register, immediate # compare register and immediate (set condition codes)
|
||||
test register, register # compare register and register (set condition codes)
|
||||
|
||||
jne # jump if test'd values are not equal
|
||||
je # jump if test'd values are equal
|
||||
jlt # jump if test'd second is less than first
|
||||
jlte # jump if test'd second is less than or equal first
|
||||
jgt # jump if test'd second is greater than first
|
||||
jgte # jump if test'd second is greater than or equal first
|
||||
|
||||
xchg register, memory # atomic exchange:
|
||||
# put value of register into memory
|
||||
# return old contents of memory into reg
|
||||
# do both things atomically
|
||||
|
||||
nop # no op
|
||||
halt # stop
|
||||
push memory or register # push value in memory or from reg onto stack
|
||||
# stack is defined by sp register
|
||||
pop [register] # pop value off stack (into optional register)
|
||||
call label # call function at label
|
||||
yield # switch to the next thread in the runqueue
|
||||
```
|
||||
注意:
|
||||
- 'immediate' 格式是 $number
|
||||
- 'memory' 格式是 'number' 或 '(reg)' 或 'number(reg)' 或 'number(reg,reg)'
|
||||
- (%cx) -> 在括号中的register cx 的值 形成 address
|
||||
- 2000 -> 2000 形成 address
|
||||
- 1000(%dx) -> 1000 + dx的值 形成 address
|
||||
- 10(%ax,%bx) -> 10 + ax的值 + bx的值 形成 address
|
||||
- 'register' 格式是 %ax, %bx, %cx, %dx
|
||||
|
||||
|
||||
下面是一个代码片段:
|
||||
```
|
||||
.main
|
||||
mov 2000, %ax # 取地址2000处的内存单元的内容,并赋值给ax
|
||||
add $1, %ax # ax=ax+1
|
||||
mov %ax, 2000 # 把ax的内容存储到地址2000处的内存单元中
|
||||
halt
|
||||
```
|
||||
其含义如下
|
||||
```
|
||||
2000 -> 2000 形成地址 address
|
||||
(%cx) -> cx的内容 形成地址 address
|
||||
1000(%dx) -> (1000+dx) 形成地址 address
|
||||
10(%ax,%bx) -> (10+ax+bx) 形成地址 address
|
||||
halt -> 执行结束
|
||||
```
|
||||
|
||||
循环执行的小例子片段
|
||||
```
|
||||
.main
|
||||
.top
|
||||
sub $1,%dx
|
||||
test $0,%dx
|
||||
jgte .top
|
||||
halt
|
||||
```
|
||||
|
||||
x86.py模拟器运行参数
|
||||
```
|
||||
-h, --help show this help message and exit
|
||||
-s SEED, --seed=SEED the random seed
|
||||
-t NUMTHREADS, --threads=NUMTHREADS
|
||||
number of threads
|
||||
-p PROGFILE, --program=PROGFILE
|
||||
source program (in .s)
|
||||
-i INTFREQ, --interrupt=INTFREQ
|
||||
interrupt frequency
|
||||
-r, --randints if interrupts are random
|
||||
-a ARGV, --argv=ARGV comma-separated per-thread args (e.g., ax=1,ax=2 sets
|
||||
thread 0 ax reg to 1 and thread 1 ax reg to 2);
|
||||
specify multiple regs per thread via colon-separated
|
||||
list (e.g., ax=1:bx=2,cx=3 sets thread 0 ax and bx and
|
||||
just cx for thread 1)
|
||||
-L LOADADDR, --loadaddr=LOADADDR
|
||||
address where to load code
|
||||
-m MEMSIZE, --memsize=MEMSIZE
|
||||
size of address space (KB)
|
||||
-M MEMTRACE, --memtrace=MEMTRACE
|
||||
comma-separated list of addrs to trace (e.g.,
|
||||
20000,20001)
|
||||
-R REGTRACE, --regtrace=REGTRACE
|
||||
comma-separated list of regs to trace (e.g.,
|
||||
ax,bx,cx,dx)
|
||||
-C, --cctrace should we trace condition codes
|
||||
-S, --printstats print some extra stats
|
||||
-v, --verbose print some extra info
|
||||
-c, --compute compute answers for me
|
||||
|
||||
```
|
||||
|
||||
执行举例
|
||||
```
|
||||
$ ./x86.py -p simple-race.s -t 1 -M 2000 -R ax,bx
|
||||
|
||||
2000 ax bx Thread 0
|
||||
? ? ?
|
||||
? ? ? 1000 mov 2000, %ax
|
||||
? ? ? 1001 add $1, %ax
|
||||
? ? ? 1002 mov %ax, 2000
|
||||
? ? ? 1003 halt
|
||||
```
|
||||
|
||||
如果加上参数 `-c`可得到具体执行结果
|
||||
```
|
||||
$ ./x86.py -p simple-race.s -t 1 -M 2000 -R ax,bx -c
|
||||
|
||||
2000 ax bx Thread 0
|
||||
0 0 0
|
||||
0 0 0 1000 mov 2000, %ax
|
||||
0 1 0 1001 add $1, %ax
|
||||
1 1 0 1002 mov %ax, 2000
|
||||
1 1 0 1003 halt
|
||||
```
|
||||
|
||||
另外一个执行的例子
|
||||
```
|
||||
$ ./x86.py -p loop.s -t 1 -a dx=3 -R dx -C -c
|
||||
|
||||
dx >= > <= < != == Thread 0
|
||||
3 0 0 0 0 0 0
|
||||
2 0 0 0 0 0 0 1000 sub $1,%dx
|
||||
2 1 1 0 0 1 0 1001 test $0,%dx
|
||||
2 1 1 0 0 1 0 1002 jgte .top
|
||||
1 1 1 0 0 1 0 1000 sub $1,%dx
|
||||
1 1 1 0 0 1 0 1001 test $0,%dx
|
||||
1 1 1 0 0 1 0 1002 jgte .top
|
||||
0 1 1 0 0 1 0 1000 sub $1,%dx
|
||||
0 1 0 1 0 0 1 1001 test $0,%dx
|
||||
0 1 0 1 0 0 1 1002 jgte .top
|
||||
0 1 0 1 0 0 1 1003 halt
|
||||
```
|
||||
|
||||
多线程存在race condition 的例子 looping-race-nolock.s
|
||||
```
|
||||
.main
|
||||
.top
|
||||
# critical section
|
||||
mov 2000, %ax # get the value at the address
|
||||
add $1, %ax # increment it
|
||||
mov %ax, 2000 # store it back
|
||||
|
||||
# see if we're still looping
|
||||
sub $1, %bx
|
||||
test $0, %bx
|
||||
jgt .top
|
||||
|
||||
halt
|
||||
```
|
||||
|
||||
执行结果:
|
||||
```
|
||||
$ ./x86.py -p looping-race-nolock.s -t 2 -a bx=1 -M 2000 -c
|
||||
|
||||
2000 bx Thread 0 Thread 1
|
||||
0 1
|
||||
0 1 1000 mov 2000, %ax
|
||||
0 1 1001 add $1, %ax
|
||||
1 1 1002 mov %ax, 2000
|
||||
1 0 1003 sub $1, %bx
|
||||
1 0 1004 test $0, %bx
|
||||
1 0 1005 jgt .top
|
||||
1 0 1006 halt
|
||||
1 1 ----- Halt;Switch ----- ----- Halt;Switch -----
|
||||
1 1 1000 mov 2000, %ax
|
||||
1 1 1001 add $1, %ax
|
||||
2 1 1002 mov %ax, 2000
|
||||
2 0 1003 sub $1, %bx
|
||||
2 0 1004 test $0, %bx
|
||||
2 0 1005 jgt .top
|
||||
2 0 1006 halt
|
||||
```
|
||||
|
||||
多线程存在 race condition 的例子 looping-race-nolock.s 在引入中断后,会产生race condition.
|
||||
```
|
||||
$ ./x86.py -p looping-race-nolock.s -t 2 -a bx=1 -M 2000 -i 2
|
||||
|
||||
2000 Thread 0 Thread 1
|
||||
?
|
||||
? 1000 mov 2000, %ax
|
||||
? 1001 add $1, %ax
|
||||
? ------ Interrupt ------ ------ Interrupt ------
|
||||
? 1000 mov 2000, %ax
|
||||
? 1001 add $1, %ax
|
||||
? ------ Interrupt ------ ------ Interrupt ------
|
||||
? 1002 mov %ax, 2000
|
||||
? 1003 sub $1, %bx
|
||||
? ------ Interrupt ------ ------ Interrupt ------
|
||||
? 1002 mov %ax, 2000
|
||||
? 1003 sub $1, %bx
|
||||
? ------ Interrupt ------ ------ Interrupt ------
|
||||
? 1004 test $0, %bx
|
||||
? 1005 jgt .top
|
||||
? ------ Interrupt ------ ------ Interrupt ------
|
||||
? 1004 test $0, %bx
|
||||
? 1005 jgt .top
|
||||
? ------ Interrupt ------ ------ Interrupt ------
|
||||
? 1006 halt
|
||||
? ----- Halt;Switch ----- ----- Halt;Switch -----
|
||||
? 1006 halt
|
||||
```
|
||||
|
||||
|
6
related_info/lab7/race-condition/loop.s
Normal file
6
related_info/lab7/race-condition/loop.s
Normal file
@ -0,0 +1,6 @@
|
||||
.main
|
||||
.top
|
||||
sub $1,%dx
|
||||
test $0,%dx
|
||||
jgte .top
|
||||
halt
|
15
related_info/lab7/race-condition/looping-race-nolock.s
Normal file
15
related_info/lab7/race-condition/looping-race-nolock.s
Normal file
@ -0,0 +1,15 @@
|
||||
# assumes %bx has loop count in it
|
||||
|
||||
.main
|
||||
.top
|
||||
# critical section
|
||||
mov 2000, %ax # get the value at the address
|
||||
add $1, %ax # increment it
|
||||
mov %ax, 2000 # store it back
|
||||
|
||||
# see if we're still looping
|
||||
sub $1, %bx
|
||||
test $0, %bx
|
||||
jgt .top
|
||||
|
||||
halt
|
6
related_info/lab7/race-condition/simple-race.s
Normal file
6
related_info/lab7/race-condition/simple-race.s
Normal file
@ -0,0 +1,6 @@
|
||||
.main
|
||||
# this is a critical section
|
||||
mov 2000(%bx), %ax # get the value at the address
|
||||
add $1, %ax # increment it
|
||||
mov %ax, 2000(%bx) # store it back
|
||||
halt
|
7
related_info/lab7/race-condition/test.s
Normal file
7
related_info/lab7/race-condition/test.s
Normal file
@ -0,0 +1,7 @@
|
||||
.main
|
||||
mov $9,%dx
|
||||
.top
|
||||
sub $1,%dx
|
||||
test $0,%dx
|
||||
jgte .top
|
||||
halt
|
13
related_info/lab7/race-condition/wait-for-me.s
Normal file
13
related_info/lab7/race-condition/wait-for-me.s
Normal file
@ -0,0 +1,13 @@
|
||||
.main
|
||||
test $1, %ax # ax should be 1 (signaller) or 0 (waiter)
|
||||
je .signaller
|
||||
|
||||
.waiter
|
||||
mov 2000, %cx
|
||||
test $1, %cx
|
||||
jne .waiter
|
||||
halt
|
||||
|
||||
.signaller
|
||||
mov $1, 2000
|
||||
halt
|
989
related_info/lab7/race-condition/x86.py
Executable file
989
related_info/lab7/race-condition/x86.py
Executable file
@ -0,0 +1,989 @@
|
||||
#! /usr/bin/env python
|
||||
|
||||
import sys
|
||||
import time
|
||||
import random
|
||||
from optparse import OptionParser
|
||||
|
||||
#
|
||||
# HELPER
|
||||
#
|
||||
def dospace(howmuch):
|
||||
for i in range(howmuch):
|
||||
print '%24s' % ' ',
|
||||
|
||||
# useful instead of assert
|
||||
def zassert(cond, str):
|
||||
if cond == False:
|
||||
print 'ABORT::', str
|
||||
exit(1)
|
||||
return
|
||||
|
||||
class cpu:
|
||||
#
|
||||
# INIT: how much memory?
|
||||
#
|
||||
def __init__(self, memory, memtrace, regtrace, cctrace, compute, verbose):
|
||||
#
|
||||
# CONSTANTS
|
||||
#
|
||||
|
||||
# conditions
|
||||
self.COND_GT = 0
|
||||
self.COND_GTE = 1
|
||||
self.COND_LT = 2
|
||||
self.COND_LTE = 3
|
||||
self.COND_EQ = 4
|
||||
self.COND_NEQ = 5
|
||||
|
||||
# registers in system
|
||||
self.REG_ZERO = 0
|
||||
self.REG_AX = 1
|
||||
self.REG_BX = 2
|
||||
self.REG_CX = 3
|
||||
self.REG_DX = 4
|
||||
self.REG_SP = 5
|
||||
self.REG_BP = 6
|
||||
|
||||
# system memory: in KB
|
||||
self.max_memory = memory * 1024
|
||||
|
||||
# which memory addrs and registers to trace?
|
||||
self.memtrace = memtrace
|
||||
self.regtrace = regtrace
|
||||
self.cctrace = cctrace
|
||||
self.compute = compute
|
||||
self.verbose = verbose
|
||||
|
||||
self.PC = 0
|
||||
self.registers = {}
|
||||
self.conditions = {}
|
||||
self.labels = {}
|
||||
self.vars = {}
|
||||
self.memory = {}
|
||||
self.pmemory = {} # for printable version of what's in memory (instructions)
|
||||
|
||||
self.condlist = [self.COND_GTE, self.COND_GT, self.COND_LTE, self.COND_LT, self.COND_NEQ, self.COND_EQ]
|
||||
self.regnums = [self.REG_ZERO, self.REG_AX, self.REG_BX, self.REG_CX, self.REG_DX, self.REG_SP, self.REG_BP]
|
||||
|
||||
self.regnames = {}
|
||||
self.regnames['zero'] = self.REG_ZERO # hidden zero-valued register
|
||||
self.regnames['ax'] = self.REG_AX
|
||||
self.regnames['bx'] = self.REG_BX
|
||||
self.regnames['cx'] = self.REG_CX
|
||||
self.regnames['dx'] = self.REG_DX
|
||||
self.regnames['sp'] = self.REG_SP
|
||||
self.regnames['bp'] = self.REG_BP
|
||||
|
||||
tmplist = []
|
||||
for r in self.regtrace:
|
||||
zassert(r in self.regnames, 'Register %s cannot be traced because it does not exist' % r)
|
||||
tmplist.append(self.regnames[r])
|
||||
self.regtrace = tmplist
|
||||
|
||||
self.init_memory()
|
||||
self.init_registers()
|
||||
self.init_condition_codes()
|
||||
|
||||
#
|
||||
# BEFORE MACHINE RUNS
|
||||
#
|
||||
def init_condition_codes(self):
|
||||
for c in self.condlist:
|
||||
self.conditions[c] = False
|
||||
|
||||
def init_memory(self):
|
||||
for i in range(self.max_memory):
|
||||
self.memory[i] = 0
|
||||
|
||||
def init_registers(self):
|
||||
for i in self.regnums:
|
||||
self.registers[i] = 0
|
||||
|
||||
def dump_memory(self):
|
||||
print 'MEMORY DUMP'
|
||||
for i in range(self.max_memory):
|
||||
if i not in self.pmemory and i in self.memory and self.memory[i] != 0:
|
||||
print ' m[%d]' % i, self.memory[i]
|
||||
|
||||
#
|
||||
# INFORMING ABOUT THE HARDWARE
|
||||
#
|
||||
def get_regnum(self, name):
|
||||
assert(name in self.regnames)
|
||||
return self.regnames[name]
|
||||
|
||||
def get_regname(self, num):
|
||||
assert(num in self.regnums)
|
||||
for rname in self.regnames:
|
||||
if self.regnames[rname] == num:
|
||||
return rname
|
||||
return ''
|
||||
|
||||
def get_regnums(self):
|
||||
return self.regnums
|
||||
|
||||
def get_condlist(self):
|
||||
return self.condlist
|
||||
|
||||
def get_reg(self, reg):
|
||||
assert(reg in self.regnums)
|
||||
return self.registers[reg]
|
||||
|
||||
def get_cond(self, cond):
|
||||
assert(cond in self.condlist)
|
||||
return self.conditions[cond]
|
||||
|
||||
def get_pc(self):
|
||||
return self.PC
|
||||
|
||||
def set_reg(self, reg, value):
|
||||
assert(reg in self.regnums)
|
||||
self.registers[reg] = value
|
||||
|
||||
def set_cond(self, cond, value):
|
||||
assert(cond in self.condlist)
|
||||
self.conditions[cond] = value
|
||||
|
||||
def set_pc(self, pc):
|
||||
self.PC = pc
|
||||
|
||||
#
|
||||
# INSTRUCTIONS
|
||||
#
|
||||
def halt(self):
|
||||
return -1
|
||||
|
||||
def iyield(self):
|
||||
return -2
|
||||
|
||||
def nop(self):
|
||||
return 0
|
||||
|
||||
def rdump(self):
|
||||
print 'REGISTERS::',
|
||||
print 'ax:', self.registers[self.REG_AX],
|
||||
print 'bx:', self.registers[self.REG_BX],
|
||||
print 'cx:', self.registers[self.REG_CX],
|
||||
print 'dx:', self.registers[self.REG_DX],
|
||||
|
||||
def mdump(self, index):
|
||||
print 'm[%d] ' % index, self.memory[index]
|
||||
|
||||
def move_i_to_r(self, src, dst):
|
||||
self.registers[dst] = src
|
||||
return 0
|
||||
|
||||
# memory: value, register, register
|
||||
def move_i_to_m(self, src, value, reg1, reg2):
|
||||
tmp = value + self.registers[reg1] + self.registers[reg2]
|
||||
self.memory[tmp] = src
|
||||
return 0
|
||||
|
||||
def move_m_to_r(self, value, reg1, reg2, dst):
|
||||
tmp = value + self.registers[reg1] + self.registers[reg2]
|
||||
# print 'doing mov', 'val:', value, 'r1:', self.get_regname(reg1), self.registers[reg1], 'r2:', self.get_regname(reg2), self.registers[reg2], 'dst', self.get_regname(dst), 'tmp', tmp, 'reg[dst]', self.registers[dst], 'mem', self.memory[tmp]
|
||||
self.registers[dst] = self.memory[tmp]
|
||||
|
||||
def move_r_to_m(self, src, value, reg1, reg2):
|
||||
tmp = value + self.registers[reg1] + self.registers[reg2]
|
||||
self.memory[tmp] = self.registers[src]
|
||||
return 0
|
||||
|
||||
def move_r_to_r(self, src, dst):
|
||||
self.registers[dst] = self.registers[src]
|
||||
return 0
|
||||
|
||||
def add_i_to_r(self, src, dst):
|
||||
self.registers[dst] += src
|
||||
return 0
|
||||
|
||||
def add_r_to_r(self, src, dst):
|
||||
self.registers[dst] += self.registers[src]
|
||||
return 0
|
||||
|
||||
def sub_i_to_r(self, src, dst):
|
||||
self.registers[dst] -= src
|
||||
return 0
|
||||
|
||||
def sub_r_to_r(self, src, dst):
|
||||
self.registers[dst] -= self.registers[src]
|
||||
return 0
|
||||
|
||||
|
||||
#
|
||||
# SUPPORT FOR LOCKS
|
||||
#
|
||||
def atomic_exchange(self, src, value, reg1, reg2):
|
||||
tmp = value + self.registers[reg1] + self.registers[reg2]
|
||||
old = self.memory[tmp]
|
||||
self.memory[tmp] = self.registers[src]
|
||||
self.registers[src] = old
|
||||
return 0
|
||||
|
||||
def fetchadd(self, src, value, reg1, reg2):
|
||||
tmp = value + self.registers[reg1] + self.registers[reg2]
|
||||
old = self.memory[tmp]
|
||||
self.memory[tmp] = self.memory[tmp] + self.registers[src]
|
||||
self.registers[src] = old
|
||||
|
||||
#
|
||||
# TEST for conditions
|
||||
#
|
||||
def test_all(self, src, dst):
|
||||
self.init_condition_codes()
|
||||
if dst > src:
|
||||
self.conditions[self.COND_GT] = True
|
||||
if dst >= src:
|
||||
self.conditions[self.COND_GTE] = True
|
||||
if dst < src:
|
||||
self.conditions[self.COND_LT] = True
|
||||
if dst <= src:
|
||||
self.conditions[self.COND_LTE] = True
|
||||
if dst == src:
|
||||
self.conditions[self.COND_EQ] = True
|
||||
if dst != src:
|
||||
self.conditions[self.COND_NEQ] = True
|
||||
return 0
|
||||
|
||||
def test_i_r(self, src, dst):
|
||||
self.init_condition_codes()
|
||||
return self.test_all(src, self.registers[dst])
|
||||
|
||||
def test_r_i(self, src, dst):
|
||||
self.init_condition_codes()
|
||||
return self.test_all(self.registers[src], dst)
|
||||
|
||||
def test_r_r(self, src, dst):
|
||||
self.init_condition_codes()
|
||||
return self.test_all(self.registers[src], self.registers[dst])
|
||||
|
||||
#
|
||||
# JUMPS
|
||||
#
|
||||
def jump(self, targ):
|
||||
self.PC = targ
|
||||
return 0
|
||||
|
||||
def jump_notequal(self, targ):
|
||||
if self.conditions[self.COND_NEQ] == True:
|
||||
self.PC = targ
|
||||
return 0
|
||||
|
||||
def jump_equal(self, targ):
|
||||
if self.conditions[self.COND_EQ] == True:
|
||||
self.PC = targ
|
||||
return 0
|
||||
|
||||
def jump_lessthan(self, targ):
|
||||
if self.conditions[self.COND_LT] == True:
|
||||
self.PC = targ
|
||||
return 0
|
||||
|
||||
def jump_lessthanorequal(self, targ):
|
||||
if self.conditions[self.COND_LTE] == True:
|
||||
self.PC = targ
|
||||
return 0
|
||||
|
||||
def jump_greaterthan(self, targ):
|
||||
if self.conditions[self.COND_GT] == True:
|
||||
self.PC = targ
|
||||
return 0
|
||||
|
||||
def jump_greaterthanorequal(self, targ):
|
||||
if self.conditions[self.COND_GTE] == True:
|
||||
self.PC = targ
|
||||
return 0
|
||||
|
||||
#
|
||||
# CALL and RETURN
|
||||
#
|
||||
def call(self, targ):
|
||||
self.registers[self.REG_SP] -= 4
|
||||
self.memory[self.registers[self.REG_SP]] = self.PC
|
||||
self.PC = targ
|
||||
|
||||
def ret(self):
|
||||
self.PC = self.memory[self.registers[self.REG_SP]]
|
||||
self.registers[self.REG_SP] += 4
|
||||
|
||||
#
|
||||
# STACK and related
|
||||
#
|
||||
def push_r(self, reg):
|
||||
self.registers[self.REG_SP] -= 4
|
||||
self.memory[self.registers[self.REG_SP]] = self.registers[reg]
|
||||
return 0
|
||||
|
||||
def push_m(self, value, reg1, reg2):
|
||||
# print 'push_m', value, reg1, reg2
|
||||
self.registers[self.REG_SP] -= 4
|
||||
tmp = value + self.registers[reg1] + self.registers[reg2]
|
||||
# push address onto stack, not memory value itself
|
||||
self.memory[self.registers[self.REG_SP]] = tmp
|
||||
return 0
|
||||
|
||||
def pop(self):
|
||||
self.registers[self.REG_SP] += 4
|
||||
|
||||
def pop_r(self, dst):
|
||||
self.registers[dst] = self.registers[self.REG_SP]
|
||||
self.registers[self.REG_SP] += 4
|
||||
|
||||
#
|
||||
# HELPER func for getarg
|
||||
#
|
||||
def register_translate(self, r):
|
||||
if r in self.regnames:
|
||||
return self.regnames[r]
|
||||
zassert(False, 'Register %s is not a valid register' % r)
|
||||
return
|
||||
|
||||
#
|
||||
# HELPER in parsing mov (quite primitive) and other ops
|
||||
# returns: (value, type)
|
||||
# where type is (TYPE_REGISTER, TYPE_IMMEDIATE, TYPE_MEMORY)
|
||||
#
|
||||
# FORMATS
|
||||
# %ax - register
|
||||
# $10 - immediate
|
||||
# 10 - direct memory
|
||||
# 10(%ax) - memory + reg indirect
|
||||
# 10(%ax,%bx) - memory + 2 reg indirect
|
||||
# 10(%ax,%bx,4) - XXX (not handled)
|
||||
#
|
||||
def getarg(self, arg):
|
||||
tmp1 = arg.replace(',', '')
|
||||
tmp = tmp1.replace(' \t', '')
|
||||
|
||||
if tmp[0] == '$':
|
||||
zassert(len(tmp) == 2, 'correct form is $number (not %s)' % tmp)
|
||||
value = tmp.split('$')[1]
|
||||
zassert(value.isdigit(), 'value [%s] must be a digit' % value)
|
||||
return int(value), 'TYPE_IMMEDIATE'
|
||||
elif tmp[0] == '%':
|
||||
register = tmp.split('%')[1]
|
||||
return self.register_translate(register), 'TYPE_REGISTER'
|
||||
elif tmp[0] == '(':
|
||||
register = tmp.split('(')[1].split(')')[0].split('%')[1]
|
||||
return '%d,%d,%d' % (0, self.register_translate(register), self.register_translate('zero')), 'TYPE_MEMORY'
|
||||
elif tmp[0] == '.':
|
||||
targ = tmp
|
||||
return targ, 'TYPE_LABEL'
|
||||
elif tmp[0].isalpha() and not tmp[0].isdigit():
|
||||
zassert(tmp in self.vars, 'Variable %s is not declared' % tmp)
|
||||
# print '%d,%d,%d' % (self.vars[tmp], self.register_translate('zero'), self.register_translate('zero')), 'TYPE_MEMORY'
|
||||
return '%d,%d,%d' % (self.vars[tmp], self.register_translate('zero'), self.register_translate('zero')), 'TYPE_MEMORY'
|
||||
elif tmp[0].isdigit() or tmp[0] == '-':
|
||||
# MOST GENERAL CASE: number(reg,reg) or number(reg)
|
||||
# we ignore the common x86 number(reg,reg,constant) for now
|
||||
neg = 1
|
||||
if tmp[0] == '-':
|
||||
tmp = tmp[1:]
|
||||
neg = -1
|
||||
s = tmp.split('(')
|
||||
if len(s) == 1:
|
||||
value = neg * int(tmp)
|
||||
# print '%d,%d,%d' % (int(value), self.register_translate('zero'), self.register_translate('zero')), 'TYPE_MEMORY'
|
||||
return '%d,%d,%d' % (int(value), self.register_translate('zero'), self.register_translate('zero')), 'TYPE_MEMORY'
|
||||
elif len(s) == 2:
|
||||
value = neg * int(s[0])
|
||||
t = s[1].split(')')[0].split(',')
|
||||
if len(t) == 1:
|
||||
register = t[0].split('%')[1]
|
||||
# print '%d,%d,%d' % (int(value), self.register_translate(register), self.register_translate('zero')), 'TYPE_MEMORY'
|
||||
return '%d,%d,%d' % (int(value), self.register_translate(register), self.register_translate('zero')), 'TYPE_MEMORY'
|
||||
elif len(t) == 2:
|
||||
register1 = t[0].split('%')[1]
|
||||
register2 = t[1].split('%')[1]
|
||||
# print '%d,%d,%d' % (int(value), self.register_translate(register1), self.register_translate(register2)), 'TYPE_MEMORY'
|
||||
return '%d,%d,%d' % (int(value), self.register_translate(register1), self.register_translate(register2)), 'TYPE_MEMORY'
|
||||
else:
|
||||
print 'mov: bad argument [%s]' % tmp
|
||||
exit(1)
|
||||
return
|
||||
zassert(True, 'mov: bad argument [%s]' % arg)
|
||||
return
|
||||
|
||||
#
|
||||
# LOAD a program into memory
|
||||
# make it ready to execute
|
||||
#
|
||||
def load(self, infile, loadaddr):
|
||||
pc = int(loadaddr)
|
||||
fd = open(infile)
|
||||
|
||||
bpc = loadaddr
|
||||
data = 100
|
||||
|
||||
for line in fd:
|
||||
cline = line.rstrip()
|
||||
# print 'PASS 1', cline
|
||||
|
||||
# remove everything after the comment marker
|
||||
ctmp = cline.split('#')
|
||||
assert(len(ctmp) == 1 or len(ctmp) == 2)
|
||||
if len(ctmp) == 2:
|
||||
cline = ctmp[0]
|
||||
|
||||
# remove empty lines, and split line by spaces
|
||||
tmp = cline.split()
|
||||
if len(tmp) == 0:
|
||||
continue
|
||||
|
||||
# only pay attention to labels and variables
|
||||
if tmp[0] == '.var':
|
||||
assert(len(tmp) == 2)
|
||||
assert(tmp[0] not in self.vars)
|
||||
self.vars[tmp[1]] = data
|
||||
data += 4
|
||||
zassert(data < bpc, 'Load address overrun by static data')
|
||||
if self.verbose: print 'ASSIGN VAR', tmp[0], "-->", tmp[1], self.vars[tmp[1]]
|
||||
elif tmp[0][0] == '.':
|
||||
assert(len(tmp) == 1)
|
||||
self.labels[tmp[0]] = int(pc)
|
||||
if self.verbose: print 'ASSIGN LABEL', tmp[0], "-->", pc
|
||||
else:
|
||||
pc += 1
|
||||
fd.close()
|
||||
|
||||
if self.verbose: print ''
|
||||
|
||||
# second pass: do everything else
|
||||
pc = int(loadaddr)
|
||||
fd = open(infile)
|
||||
for line in fd:
|
||||
cline = line.rstrip()
|
||||
# print 'PASS 2', cline
|
||||
|
||||
# remove everything after the comment marker
|
||||
ctmp = cline.split('#')
|
||||
assert(len(ctmp) == 1 or len(ctmp) == 2)
|
||||
if len(ctmp) == 2:
|
||||
cline = ctmp[0]
|
||||
|
||||
# remove empty lines, and split line by spaces
|
||||
tmp = cline.split()
|
||||
if len(tmp) == 0:
|
||||
continue
|
||||
|
||||
# skip labels: all else must be instructions
|
||||
if cline[0] != '.':
|
||||
tmp = cline.split(None, 1)
|
||||
opcode = tmp[0]
|
||||
self.pmemory[pc] = cline.strip()
|
||||
|
||||
# MAIN OPCODE LOOP
|
||||
if opcode == 'mov':
|
||||
rtmp = tmp[1].split(',', 1)
|
||||
zassert(len(tmp) == 2 and len(rtmp) == 2, 'mov: needs two args, separated by commas [%s]' % cline)
|
||||
arg1 = rtmp[0].strip()
|
||||
arg2 = rtmp[1].strip()
|
||||
(src, stype) = self.getarg(arg1)
|
||||
(dst, dtype) = self.getarg(arg2)
|
||||
# print 'MOV', src, stype, dst, dtype
|
||||
if stype == 'TYPE_MEMORY' and dtype == 'TYPE_MEMORY':
|
||||
print 'bad mov: two memory arguments'
|
||||
exit(1)
|
||||
elif stype == 'TYPE_IMMEDIATE' and dtype == 'TYPE_IMMEDIATE':
|
||||
print 'bad mov: two immediate arguments'
|
||||
exit(1)
|
||||
elif stype == 'TYPE_IMMEDIATE' and dtype == 'TYPE_REGISTER':
|
||||
self.memory[pc] = 'self.move_i_to_r(%d, %d)' % (int(src), dst)
|
||||
elif stype == 'TYPE_IMMEDIATE' and dtype == 'TYPE_REGISTER':
|
||||
self.memory[pc] = 'self.move_i_to_r(%d, %d)' % (int(src), dst)
|
||||
elif stype == 'TYPE_MEMORY' and dtype == 'TYPE_REGISTER':
|
||||
tmp = src.split(',')
|
||||
assert(len(tmp) == 3)
|
||||
self.memory[pc] = 'self.move_m_to_r(%d, %d, %d, %d)' % (int(tmp[0]), int(tmp[1]), int(tmp[2]), dst)
|
||||
elif stype == 'TYPE_REGISTER' and dtype == 'TYPE_MEMORY':
|
||||
tmp = dst.split(',')
|
||||
assert(len(tmp) == 3)
|
||||
self.memory[pc] = 'self.move_r_to_m(%d, %d, %d, %d)' % (src, int(tmp[0]), int(tmp[1]), int(tmp[2]))
|
||||
elif stype == 'TYPE_REGISTER' and dtype == 'TYPE_REGISTER':
|
||||
self.memory[pc] = 'self.move_r_to_r(%d, %d)' % (src, dst)
|
||||
elif stype == 'TYPE_IMMEDIATE' and dtype == 'TYPE_MEMORY':
|
||||
tmp = dst.split(',')
|
||||
assert(len(tmp) == 3)
|
||||
self.memory[pc] = 'self.move_i_to_m(%d, %d, %d, %d)' % (src, int(tmp[0]), int(tmp[1]), int(tmp[2]))
|
||||
else:
|
||||
zassert(False, 'malformed mov instruction')
|
||||
elif opcode == 'pop':
|
||||
if len(tmp) == 1:
|
||||
self.memory[pc] = 'self.pop()'
|
||||
elif len(tmp) == 2:
|
||||
arg = tmp[1].strip()
|
||||
(dst, dtype) = self.getarg(arg)
|
||||
zassert(dtype == 'TYPE_REGISTER', 'Can only pop into a register')
|
||||
self.memory[pc] = 'self.pop_r(%d)' % dst
|
||||
else:
|
||||
zassert(False, 'pop instruction must take zero/one args')
|
||||
elif opcode == 'push':
|
||||
(src, stype) = self.getarg(tmp[1].strip())
|
||||
if stype == 'TYPE_REGISTER':
|
||||
self.memory[pc] = 'self.push_r(%d)' % (int(src))
|
||||
elif stype == 'TYPE_MEMORY':
|
||||
tmp = src.split(',')
|
||||
assert(len(tmp) == 3)
|
||||
self.memory[pc] = 'self.push_m(%d,%d,%d)' % (int(tmp[0]), int(tmp[1]), int(tmp[2]))
|
||||
else:
|
||||
zassert(False, 'Cannot push anything but registers')
|
||||
elif opcode == 'call':
|
||||
(targ, ttype) = self.getarg(tmp[1].strip())
|
||||
if ttype == 'TYPE_LABEL':
|
||||
self.memory[pc] = 'self.call(%d)' % (int(self.labels[targ]))
|
||||
else:
|
||||
zassert(False, 'Cannot call anything but a label')
|
||||
elif opcode == 'ret':
|
||||
assert(len(tmp) == 1)
|
||||
self.memory[pc] = 'self.ret()'
|
||||
elif opcode == 'add':
|
||||
rtmp = tmp[1].split(',', 1)
|
||||
zassert(len(tmp) == 2 and len(rtmp) == 2, 'add: needs two args, separated by commas [%s]' % cline)
|
||||
arg1 = rtmp[0].strip()
|
||||
arg2 = rtmp[1].strip()
|
||||
(src, stype) = self.getarg(arg1)
|
||||
(dst, dtype) = self.getarg(arg2)
|
||||
if stype == 'TYPE_IMMEDIATE' and dtype == 'TYPE_REGISTER':
|
||||
self.memory[pc] = 'self.add_i_to_r(%d, %d)' % (int(src), dst)
|
||||
elif stype == 'TYPE_REGISTER' and dtype == 'TYPE_REGISTER':
|
||||
self.memory[pc] = 'self.add_r_to_r(%d, %d)' % (int(src), dst)
|
||||
else:
|
||||
zassert(False, 'malformed usage of add instruction')
|
||||
elif opcode == 'sub':
|
||||
rtmp = tmp[1].split(',', 1)
|
||||
zassert(len(tmp) == 2 and len(rtmp) == 2, 'sub: needs two args, separated by commas [%s]' % cline)
|
||||
arg1 = rtmp[0].strip()
|
||||
arg2 = rtmp[1].strip()
|
||||
(src, stype) = self.getarg(arg1)
|
||||
(dst, dtype) = self.getarg(arg2)
|
||||
if stype == 'TYPE_IMMEDIATE' and dtype == 'TYPE_REGISTER':
|
||||
self.memory[pc] = 'self.sub_i_to_r(%d, %d)' % (int(src), dst)
|
||||
elif stype == 'TYPE_REGISTER' and dtype == 'TYPE_REGISTER':
|
||||
self.memory[pc] = 'self.sub_r_to_r(%d, %d)' % (int(src), dst)
|
||||
else:
|
||||
zassert(False, 'malformed usage of sub instruction')
|
||||
elif opcode == 'fetchadd':
|
||||
rtmp = tmp[1].split(',', 1)
|
||||
zassert(len(tmp) == 2 and len(rtmp) == 2, 'fetchadd: needs two args, separated by commas [%s]' % cline)
|
||||
arg1 = rtmp[0].strip()
|
||||
arg2 = rtmp[1].strip()
|
||||
(src, stype) = self.getarg(arg1)
|
||||
(dst, dtype) = self.getarg(arg2)
|
||||
tmp = dst.split(',')
|
||||
assert(len(tmp) == 3)
|
||||
if stype == 'TYPE_REGISTER' and dtype == 'TYPE_MEMORY':
|
||||
self.memory[pc] = 'self.fetchadd(%d, %d, %d, %d)' % (src, int(tmp[0]), int(tmp[1]), int(tmp[2]))
|
||||
else:
|
||||
zassert(False, 'poorly specified fetch and add')
|
||||
elif opcode == 'xchg':
|
||||
rtmp = tmp[1].split(',', 1)
|
||||
zassert(len(tmp) == 2 and len(rtmp) == 2, 'xchg: needs two args, separated by commas [%s]' % cline)
|
||||
arg1 = rtmp[0].strip()
|
||||
arg2 = rtmp[1].strip()
|
||||
(src, stype) = self.getarg(arg1)
|
||||
(dst, dtype) = self.getarg(arg2)
|
||||
tmp = dst.split(',')
|
||||
assert(len(tmp) == 3)
|
||||
if stype == 'TYPE_REGISTER' and dtype == 'TYPE_MEMORY':
|
||||
self.memory[pc] = 'self.atomic_exchange(%d, %d, %d, %d)' % (src, int(tmp[0]), int(tmp[1]), int(tmp[2]))
|
||||
else:
|
||||
zassert(False, 'poorly specified atomic exchange')
|
||||
elif opcode == 'test':
|
||||
rtmp = tmp[1].split(',', 1)
|
||||
zassert(len(tmp) == 2 and len(rtmp) == 2, 'test: needs two args, separated by commas [%s]' % cline)
|
||||
arg1 = rtmp[0].strip()
|
||||
arg2 = rtmp[1].strip()
|
||||
(src, stype) = self.getarg(arg1)
|
||||
(dst, dtype) = self.getarg(arg2)
|
||||
if stype == 'TYPE_IMMEDIATE' and dtype == 'TYPE_REGISTER':
|
||||
self.memory[pc] = 'self.test_i_r(%d, %d)' % (int(src), dst)
|
||||
elif stype == 'TYPE_REGISTER' and dtype == 'TYPE_REGISTER':
|
||||
self.memory[pc] = 'self.test_r_r(%d, %d)' % (int(src), dst)
|
||||
elif stype == 'TYPE_REGISTER' and dtype == 'TYPE_IMMEDIATE':
|
||||
self.memory[pc] = 'self.test_r_i(%d, %d)' % (int(src), dst)
|
||||
else:
|
||||
zassert(False, 'malformed usage of test instruction')
|
||||
elif opcode == 'j':
|
||||
(targ, ttype) = self.getarg(tmp[1].strip())
|
||||
zassert(ttype == 'TYPE_LABEL', 'bad jump target [%s]' % tmp[1].strip())
|
||||
self.memory[pc] = 'self.jump(%d)' % int(self.labels[targ])
|
||||
elif opcode == 'jne':
|
||||
(targ, ttype) = self.getarg(tmp[1].strip())
|
||||
zassert(ttype == 'TYPE_LABEL', 'bad jump target [%s]' % tmp[1].strip())
|
||||
self.memory[pc] = 'self.jump_notequal(%d)' % int(self.labels[targ])
|
||||
elif opcode == 'je':
|
||||
(targ, ttype) = self.getarg(tmp[1].strip())
|
||||
zassert(ttype == 'TYPE_LABEL', 'bad jump target [%s]' % tmp[1].strip())
|
||||
self.memory[pc] = 'self.jump_equal(%d)' % self.labels[targ]
|
||||
elif opcode == 'jlt':
|
||||
(targ, ttype) = self.getarg(tmp[1].strip())
|
||||
zassert(ttype == 'TYPE_LABEL', 'bad jump target [%s]' % tmp[1].strip())
|
||||
self.memory[pc] = 'self.jump_lessthan(%d)' % int(self.labels[targ])
|
||||
elif opcode == 'jlte':
|
||||
(targ, ttype) = self.getarg(tmp[1].strip())
|
||||
zassert(ttype == 'TYPE_LABEL', 'bad jump target [%s]' % tmp[1].strip())
|
||||
self.memory[pc] = 'self.jump_lessthanorequal(%s)' % self.labels[targ]
|
||||
elif opcode == 'jgt':
|
||||
(targ, ttype) = self.getarg(tmp[1].strip())
|
||||
zassert(ttype == 'TYPE_LABEL', 'bad jump target [%s]' % tmp[1].strip())
|
||||
self.memory[pc] = 'self.jump_greaterthan(%d)' % int(self.labels[targ])
|
||||
elif opcode == 'jgte':
|
||||
(targ, ttype) = self.getarg(tmp[1].strip())
|
||||
zassert(ttype == 'TYPE_LABEL', 'bad jump target [%s]' % tmp[1].strip())
|
||||
self.memory[pc] = 'self.jump_greaterthanorequal(%s)' % self.labels[targ]
|
||||
elif opcode == 'nop':
|
||||
self.memory[pc] = 'self.nop()'
|
||||
elif opcode == 'halt':
|
||||
self.memory[pc] = 'self.halt()'
|
||||
elif opcode == 'yield':
|
||||
self.memory[pc] = 'self.iyield()'
|
||||
elif opcode == 'rdump':
|
||||
self.memory[pc] = 'self.rdump()'
|
||||
elif opcode == 'mdump':
|
||||
self.memory[pc] = 'self.mdump(%s)' % tmp[1]
|
||||
else:
|
||||
print 'illegal opcode: ', opcode
|
||||
exit(1)
|
||||
|
||||
if self.verbose: print 'pc:%d LOADING %20s --> %s' % (pc, self.pmemory[pc], self.memory[pc])
|
||||
|
||||
# INCREMENT PC for loader
|
||||
pc += 1
|
||||
# END: loop over file
|
||||
fd.close()
|
||||
if self.verbose: print ''
|
||||
return
|
||||
# END: load
|
||||
|
||||
def print_headers(self, procs):
|
||||
# print some headers
|
||||
if len(self.memtrace) > 0:
|
||||
for m in self.memtrace:
|
||||
if m[0].isdigit():
|
||||
print '%5d' % int(m),
|
||||
else:
|
||||
zassert(m in self.vars, 'Traced variable %s not declared' % m)
|
||||
print '%5s' % m,
|
||||
print ' ',
|
||||
if len(self.regtrace) > 0:
|
||||
for r in self.regtrace:
|
||||
print '%5s' % self.get_regname(r),
|
||||
print ' ',
|
||||
if cctrace == True:
|
||||
print '>= > <= < != ==',
|
||||
|
||||
# and per thread
|
||||
for i in range(procs.getnum()):
|
||||
print ' Thread %d ' % i,
|
||||
print ''
|
||||
return
|
||||
|
||||
def print_trace(self, newline):
|
||||
if len(self.memtrace) > 0:
|
||||
for m in self.memtrace:
|
||||
if self.compute:
|
||||
if m[0].isdigit():
|
||||
print '%5d' % self.memory[int(m)],
|
||||
else:
|
||||
zassert(m in self.vars, 'Traced variable %s not declared' % m)
|
||||
print '%5d' % self.memory[self.vars[m]],
|
||||
else:
|
||||
print '%5s' % '?',
|
||||
print ' ',
|
||||
if len(self.regtrace) > 0:
|
||||
for r in self.regtrace:
|
||||
if self.compute:
|
||||
print '%5d' % self.registers[r],
|
||||
else:
|
||||
print '%5s' % '?',
|
||||
print ' ',
|
||||
if cctrace == True:
|
||||
for c in self.condlist:
|
||||
if self.compute:
|
||||
if self.conditions[c]:
|
||||
print '1 ',
|
||||
else:
|
||||
print '0 ',
|
||||
else:
|
||||
print '? ',
|
||||
if (len(self.memtrace) > 0 or len(self.regtrace) > 0 or cctrace == True) and newline == True:
|
||||
print ''
|
||||
return
|
||||
|
||||
def setint(self, intfreq, intrand):
|
||||
if intrand == False:
|
||||
return intfreq
|
||||
return int(random.random() * intfreq) + 1
|
||||
|
||||
def run(self, procs, intfreq, intrand):
|
||||
# hw init: cc's, interrupt frequency, etc.
|
||||
interrupt = self.setint(intfreq, intrand)
|
||||
icount = 0
|
||||
|
||||
self.print_headers(procs)
|
||||
self.print_trace(True)
|
||||
|
||||
while True:
|
||||
# need thread ID of current process
|
||||
tid = procs.getcurr().gettid()
|
||||
|
||||
# FETCH
|
||||
prevPC = self.PC
|
||||
instruction = self.memory[self.PC]
|
||||
self.PC += 1
|
||||
|
||||
# DECODE and EXECUTE
|
||||
# key: self.PC may be changed during eval; thus MUST be incremented BEFORE eval
|
||||
rc = eval(instruction)
|
||||
|
||||
# tracing details: ALWAYS AFTER EXECUTION OF INSTRUCTION
|
||||
self.print_trace(False)
|
||||
|
||||
# output: thread-proportional spacing followed by PC and instruction
|
||||
dospace(tid)
|
||||
print prevPC, self.pmemory[prevPC]
|
||||
icount += 1
|
||||
|
||||
# halt instruction issued
|
||||
if rc == -1:
|
||||
procs.done()
|
||||
if procs.numdone() == procs.getnum():
|
||||
return icount
|
||||
procs.next()
|
||||
procs.restore()
|
||||
|
||||
self.print_trace(False)
|
||||
for i in range(procs.getnum()):
|
||||
print '----- Halt;Switch ----- ',
|
||||
print ''
|
||||
|
||||
# do interrupt processing
|
||||
interrupt -= 1
|
||||
if interrupt == 0 or rc == -2:
|
||||
interrupt = self.setint(intfreq, intrand)
|
||||
procs.save()
|
||||
procs.next()
|
||||
procs.restore()
|
||||
|
||||
self.print_trace(False)
|
||||
for i in range(procs.getnum()):
|
||||
print '------ Interrupt ------ ',
|
||||
print ''
|
||||
# END: while
|
||||
return
|
||||
|
||||
#
|
||||
# END: class cpu
|
||||
#
|
||||
|
||||
|
||||
#
|
||||
# PROCESS LIST class
|
||||
#
|
||||
class proclist:
|
||||
def __init__(self):
|
||||
self.plist = []
|
||||
self.curr = 0
|
||||
self.active = 0
|
||||
|
||||
def done(self):
|
||||
self.plist[self.curr].setdone()
|
||||
self.active -= 1
|
||||
|
||||
def numdone(self):
|
||||
return len(self.plist) - self.active
|
||||
|
||||
def getnum(self):
|
||||
return len(self.plist)
|
||||
|
||||
def add(self, p):
|
||||
self.active += 1
|
||||
self.plist.append(p)
|
||||
|
||||
def getcurr(self):
|
||||
return self.plist[self.curr]
|
||||
|
||||
def save(self):
|
||||
self.plist[self.curr].save()
|
||||
|
||||
def restore(self):
|
||||
self.plist[self.curr].restore()
|
||||
|
||||
def next(self):
|
||||
for i in range(self.curr+1, len(self.plist)):
|
||||
if self.plist[i].isdone() == False:
|
||||
self.curr = i
|
||||
return
|
||||
for i in range(0, self.curr+1):
|
||||
if self.plist[i].isdone() == False:
|
||||
self.curr = i
|
||||
return
|
||||
|
||||
#
|
||||
# PROCESS class
|
||||
#
|
||||
class process:
|
||||
def __init__(self, cpu, tid, pc, stackbottom, reginit):
|
||||
self.cpu = cpu # object reference
|
||||
self.tid = tid
|
||||
self.pc = pc
|
||||
self.regs = {}
|
||||
self.cc = {}
|
||||
self.done = False
|
||||
self.stack = stackbottom
|
||||
|
||||
# init regs: all 0 or specially set to something
|
||||
for r in self.cpu.get_regnums():
|
||||
self.regs[r] = 0
|
||||
if reginit != '':
|
||||
# form: ax=1,bx=2 (for some subset of registers)
|
||||
for r in reginit.split(':'):
|
||||
tmp = r.split('=')
|
||||
assert(len(tmp) == 2)
|
||||
self.regs[self.cpu.get_regnum(tmp[0])] = int(tmp[1])
|
||||
|
||||
# init CCs
|
||||
for c in self.cpu.get_condlist():
|
||||
self.cc[c] = False
|
||||
|
||||
# stack
|
||||
self.regs[self.cpu.get_regnum('sp')] = stackbottom
|
||||
# print 'REG', self.cpu.get_regnum('sp'), self.regs[self.cpu.get_regnum('sp')]
|
||||
|
||||
return
|
||||
|
||||
def gettid(self):
|
||||
return self.tid
|
||||
|
||||
def save(self):
|
||||
self.pc = self.cpu.get_pc()
|
||||
for c in self.cpu.get_condlist():
|
||||
self.cc[c] = self.cpu.get_cond(c)
|
||||
for r in self.cpu.get_regnums():
|
||||
self.regs[r] = self.cpu.get_reg(r)
|
||||
|
||||
def restore(self):
|
||||
self.cpu.set_pc(self.pc)
|
||||
for c in self.cpu.get_condlist():
|
||||
self.cpu.set_cond(c, self.cc[c])
|
||||
for r in self.cpu.get_regnums():
|
||||
self.cpu.set_reg(r, self.regs[r])
|
||||
|
||||
def setdone(self):
|
||||
self.done = True
|
||||
|
||||
def isdone(self):
|
||||
return self.done == True
|
||||
|
||||
#
|
||||
# main program
|
||||
#
|
||||
parser = OptionParser()
|
||||
parser.add_option('-s', '--seed', default=0, help='the random seed', action='store', type='int', dest='seed')
|
||||
parser.add_option('-t', '--threads', default=2, help='number of threads', action='store', type='int', dest='numthreads')
|
||||
parser.add_option('-p', '--program', default='', help='source program (in .s)', action='store', type='string', dest='progfile')
|
||||
parser.add_option('-i', '--interrupt', default=50, help='interrupt frequency', action='store', type='int', dest='intfreq')
|
||||
parser.add_option('-r', '--randints', default=False, help='if interrupts are random', action='store_true', dest='intrand')
|
||||
parser.add_option('-a', '--argv', default='',
|
||||
help='comma-separated per-thread args (e.g., ax=1,ax=2 sets thread 0 ax reg to 1 and thread 1 ax reg to 2); specify multiple regs per thread via colon-separated list (e.g., ax=1:bx=2,cx=3 sets thread 0 ax and bx and just cx for thread 1)',
|
||||
action='store', type='string', dest='argv')
|
||||
parser.add_option('-L', '--loadaddr', default=1000, help='address where to load code', action='store', type='int', dest='loadaddr')
|
||||
parser.add_option('-m', '--memsize', default=128, help='size of address space (KB)', action='store', type='int', dest='memsize')
|
||||
parser.add_option('-M', '--memtrace', default='', help='comma-separated list of addrs to trace (e.g., 20000,20001)', action='store',
|
||||
type='string', dest='memtrace')
|
||||
parser.add_option('-R', '--regtrace', default='', help='comma-separated list of regs to trace (e.g., ax,bx,cx,dx)', action='store',
|
||||
type='string', dest='regtrace')
|
||||
parser.add_option('-C', '--cctrace', default=False, help='should we trace condition codes', action='store_true', dest='cctrace')
|
||||
parser.add_option('-S', '--printstats',default=False, help='print some extra stats', action='store_true', dest='printstats')
|
||||
parser.add_option('-v', '--verbose', default=False, help='print some extra info', action='store_true', dest='verbose')
|
||||
parser.add_option('-c', '--compute', default=False, help='compute answers for me', action='store_true', dest='solve')
|
||||
(options, args) = parser.parse_args()
|
||||
|
||||
print 'ARG seed', options.seed
|
||||
print 'ARG numthreads', options.numthreads
|
||||
print 'ARG program', options.progfile
|
||||
print 'ARG interrupt frequency', options.intfreq
|
||||
print 'ARG interrupt randomness',options.intrand
|
||||
print 'ARG argv', options.argv
|
||||
print 'ARG load address', options.loadaddr
|
||||
print 'ARG memsize', options.memsize
|
||||
print 'ARG memtrace', options.memtrace
|
||||
print 'ARG regtrace', options.regtrace
|
||||
print 'ARG cctrace', options.cctrace
|
||||
print 'ARG printstats', options.printstats
|
||||
print 'ARG verbose', options.verbose
|
||||
print ''
|
||||
|
||||
seed = int(options.seed)
|
||||
numthreads = int(options.numthreads)
|
||||
intfreq = int(options.intfreq)
|
||||
zassert(intfreq > 0, 'Interrupt frequency must be greater than 0')
|
||||
intrand = int(options.intrand)
|
||||
progfile = options.progfile
|
||||
zassert(progfile != '', 'Program file must be specified')
|
||||
argv = options.argv.split(',')
|
||||
zassert(len(argv) == numthreads or len(argv) == 1, 'argv: must be one per-thread or just one set of values for all threads')
|
||||
|
||||
loadaddr = options.loadaddr
|
||||
memsize = options.memsize
|
||||
|
||||
memtrace = []
|
||||
if options.memtrace != '':
|
||||
for m in options.memtrace.split(','):
|
||||
memtrace.append(m)
|
||||
|
||||
regtrace = []
|
||||
if options.regtrace != '':
|
||||
for r in options.regtrace.split(','):
|
||||
regtrace.append(r)
|
||||
|
||||
cctrace = options.cctrace
|
||||
|
||||
printstats = options.printstats
|
||||
verbose = options.verbose
|
||||
|
||||
#
|
||||
# MAIN program
|
||||
#
|
||||
debug = False
|
||||
debug = False
|
||||
|
||||
cpu = cpu(memsize, memtrace, regtrace, cctrace, options.solve, verbose)
|
||||
|
||||
# load a program
|
||||
cpu.load(progfile, loadaddr)
|
||||
|
||||
# process list
|
||||
procs = proclist()
|
||||
pid = 0
|
||||
stack = memsize * 1000
|
||||
for t in range(numthreads):
|
||||
if len(argv) > 1:
|
||||
arg = argv[pid]
|
||||
else:
|
||||
arg = argv[0]
|
||||
procs.add(process(cpu, pid, loadaddr, stack, arg))
|
||||
stack -= 1000
|
||||
pid += 1
|
||||
|
||||
# get first one ready!
|
||||
procs.restore()
|
||||
|
||||
# run it
|
||||
t1 = time.clock()
|
||||
ic = cpu.run(procs, intfreq, intrand)
|
||||
t2 = time.clock()
|
||||
|
||||
if printstats:
|
||||
print ''
|
||||
print 'STATS:: Instructions %d' % ic
|
||||
print 'STATS:: Emulation Rate %.2f kinst/sec' % (float(ic) / float(t2 - t1) / 1000.0)
|
||||
|
||||
# use this for profiling
|
||||
# import cProfile
|
||||
# cProfile.run('run()')
|
||||
|
||||
|
||||
|
||||
|
27
related_info/lab7/software-hardware-locks/flag.s
Normal file
27
related_info/lab7/software-hardware-locks/flag.s
Normal file
@ -0,0 +1,27 @@
|
||||
.var flag
|
||||
.var count
|
||||
|
||||
.main
|
||||
.top
|
||||
|
||||
.acquire
|
||||
mov flag, %ax # get flag
|
||||
test $0, %ax # if we get 0 back: lock is free!
|
||||
jne .acquire # if not, try again
|
||||
mov $1, flag # store 1 into flag
|
||||
|
||||
# critical section
|
||||
mov count, %ax # get the value at the address
|
||||
add $1, %ax # increment it
|
||||
mov %ax, count # store it back
|
||||
|
||||
# release lock
|
||||
mov $0, flag # clear the flag now
|
||||
|
||||
# see if we're still looping
|
||||
sub $1, %bx
|
||||
test $0, %bx
|
||||
jgt .top
|
||||
|
||||
halt
|
||||
|
53
related_info/lab7/software-hardware-locks/peterson.s
Normal file
53
related_info/lab7/software-hardware-locks/peterson.s
Normal file
@ -0,0 +1,53 @@
|
||||
# array of 2 integers (each size 4 bytes)
|
||||
# load address of flag into fx register
|
||||
# access flag[] with 0(%fx,%index,4)
|
||||
# where %index is a register holding 0 or 1
|
||||
# index reg contains 0 -> flag[0], if 1->flag[1]
|
||||
.var flag 2
|
||||
|
||||
# global turn variable
|
||||
.var turn
|
||||
|
||||
# global count
|
||||
.var count
|
||||
|
||||
.main
|
||||
|
||||
# put address of flag into fx
|
||||
lea flag, %fx
|
||||
|
||||
# assume thread ID is in bx (0 or 1, scale by 4 to get proper flag address)
|
||||
mov %bx, %cx # bx: self, now copies to cx
|
||||
neg %cx # cx: - self
|
||||
add $1, %cx # cx: 1 - self
|
||||
|
||||
.acquire
|
||||
mov $1, 0(%fx,%bx,4) # flag[self] = 1
|
||||
mov %cx, turn # turn = 1 - self
|
||||
|
||||
.spin1
|
||||
mov 0(%fx,%cx,4), %ax # flag[1-self]
|
||||
test $1, %ax
|
||||
jne .fini # if flag[1-self] != 1, skip past loop to .fini
|
||||
|
||||
.spin2 # just labeled for fun, not needed
|
||||
mov turn, %ax
|
||||
test %cx, %ax # compare 'turn' and '1 - self'
|
||||
je .spin1 # if turn==1-self, go back and start spin again
|
||||
|
||||
# fall out of spin
|
||||
.fini
|
||||
|
||||
# do critical section now
|
||||
mov count, %ax
|
||||
add $1, %ax
|
||||
mov %ax, count
|
||||
|
||||
.release
|
||||
mov $0, 0(%fx,%bx,4) # flag[self] = 0
|
||||
|
||||
|
||||
# end case: make sure it's other's turn
|
||||
mov %cx, turn # turn = 1 - self
|
||||
halt
|
||||
|
26
related_info/lab7/software-hardware-locks/test-and-set.s
Normal file
26
related_info/lab7/software-hardware-locks/test-and-set.s
Normal file
@ -0,0 +1,26 @@
|
||||
.var mutex
|
||||
.var count
|
||||
|
||||
.main
|
||||
.top
|
||||
|
||||
.acquire
|
||||
mov $1, %ax
|
||||
xchg %ax, mutex # atomic swap of 1 and mutex
|
||||
test $0, %ax # if we get 0 back: lock is free!
|
||||
jne .acquire # if not, try again
|
||||
|
||||
# critical section
|
||||
mov count, %ax # get the value at the address
|
||||
add $1, %ax # increment it
|
||||
mov %ax, count # store it back
|
||||
|
||||
# release lock
|
||||
mov $0, mutex
|
||||
|
||||
# see if we're still looping
|
||||
sub $1, %bx
|
||||
test $0, %bx
|
||||
jgt .top
|
||||
|
||||
halt
|
@ -0,0 +1,29 @@
|
||||
.var mutex
|
||||
.var count
|
||||
|
||||
.main
|
||||
.top
|
||||
|
||||
.acquire
|
||||
mov mutex, %ax
|
||||
test $0, %ax
|
||||
jne .acquire
|
||||
mov $1, %ax
|
||||
xchg %ax, mutex # atomic swap of 1 and mutex
|
||||
test $0, %ax # if we get 0 back: lock is free!
|
||||
jne .acquire # if not, try again
|
||||
|
||||
# critical section
|
||||
mov count, %ax # get the value at the address
|
||||
add $1, %ax # increment it
|
||||
mov %ax, count # store it back
|
||||
|
||||
# release lock
|
||||
mov $0, mutex
|
||||
|
||||
# see if we're still looping
|
||||
sub $1, %bx
|
||||
test $0, %bx
|
||||
jgt .top
|
||||
|
||||
halt
|
30
related_info/lab7/software-hardware-locks/ticket.s
Normal file
30
related_info/lab7/software-hardware-locks/ticket.s
Normal file
@ -0,0 +1,30 @@
|
||||
.var ticket
|
||||
.var turn
|
||||
.var count
|
||||
|
||||
.main
|
||||
.top
|
||||
|
||||
.acquire
|
||||
mov $1, %ax
|
||||
fetchadd %ax, ticket # grab a ticket (keep it in dx)
|
||||
.tryagain
|
||||
mov turn, %cx # check if it's your turn
|
||||
test %cx, %ax
|
||||
jne .tryagain
|
||||
|
||||
# critical section
|
||||
mov count, %ax # get the value at the address
|
||||
add $1, %ax # increment it
|
||||
mov %ax, count # store it back
|
||||
|
||||
# release lock
|
||||
mov $1, %ax
|
||||
fetchadd %ax, turn
|
||||
|
||||
# see if we're still looping
|
||||
sub $1, %bx
|
||||
test $0, %bx
|
||||
jgt .top
|
||||
|
||||
halt
|
1186
related_info/lab7/software-hardware-locks/x86.py
Executable file
1186
related_info/lab7/software-hardware-locks/x86.py
Executable file
File diff suppressed because it is too large
Load Diff
29
related_info/lab7/software-hardware-locks/yield.s
Normal file
29
related_info/lab7/software-hardware-locks/yield.s
Normal file
@ -0,0 +1,29 @@
|
||||
.var mutex
|
||||
.var count
|
||||
|
||||
.main
|
||||
.top
|
||||
|
||||
.acquire
|
||||
mov $1, %ax
|
||||
xchg %ax, mutex # atomic swap of 1 and mutex
|
||||
test $0, %ax # if we get 0 back: lock is free!
|
||||
je .acquire_done
|
||||
yield # if not, yield and try again
|
||||
j .acquire
|
||||
.acquire_done
|
||||
|
||||
# critical section
|
||||
mov count, %ax # get the value at the address
|
||||
add $1, %ax # increment it
|
||||
mov %ax, count # store it back
|
||||
|
||||
# release lock
|
||||
mov $0, mutex
|
||||
|
||||
# see if we're still looping
|
||||
sub $1, %bx
|
||||
test $0, %bx
|
||||
jgt .top
|
||||
|
||||
halt
|
Loading…
Reference in New Issue
Block a user