Commit c9400bea authored by Roberto Hexsel's avatar Roberto Hexsel

added program that turns the board into a clock

parent 1ad34d5e
......@@ -86,7 +86,7 @@ while true ; do
;;
-n) names=true
;;
-mif|-m) miffile=true
-mif | -m | -syn ) miffile=true
;;
-x) set -x
;;
......@@ -103,6 +103,14 @@ if [ -z $inp ] ; then usage ; exit 1 ; fi
pkg_vhd="${srcVHDL}"/packageMemory.vhd
if [ $miffile = true ]; then
S="-D FOR_SYNTHESIS" ;
touch $pkg_vhd -r ${srcVHDL}/packageMemory_fpga.vhd
else
S="-U FOR_SYNTHESIS" ;
touch $pkg_vhd -r ${srcVHDL}/packageMemory_simu.vhd
fi
if [ $pkg_vhd -nt $c_ld -o $pkg_vhd -nt $c_s ] ; then
"${bin}"/edMemory.sh -v || errorED || exit 1
fi
......
......@@ -84,9 +84,8 @@ while true ; do
;;
-n) names=false
;;
-mif) miffile=true
;;
-syn) synth=true
-mif | -syn ) synth=true
miffile=true
;;
-x) set -x
;;
......@@ -111,10 +110,22 @@ c_h="${include}"/cMIPS.h
c_s="${include}"/cMIPS.s
c_io="${include}"/cMIPSio
# c_start="${include}"/start ## see below for synthesis version
c_hndlrs="${include}"/handlers
# c_hndlrs="${include}"/handlers ## see below for synthesis version
pkg_vhd="${srcVHDL}"/packageMemory.vhd
if [ $synth = true ]; then
S="-D FOR_SYNTHESIS" ;
c_start="${include}"/syn_start
c_hndlrs="${include}"/syn_handlers
touch $pkg_vhd -r ${srcVHDL}/packageMemory_fpga.vhd
else
S="-U FOR_SYNTHESIS" ;
c_start="${include}"/start
c_hndlrs="${include}"/handlers
touch $pkg_vhd -r ${srcVHDL}/packageMemory_simu.vhd
fi
if [ $pkg_vhd -nt $c_h -o\
$pkg_vhd -nt $c_ld -o\
$pkg_vhd -nt $c_s ] ; then
......@@ -130,13 +141,6 @@ dat=data.bin
if [ $verbose = true ]; then memory_map="-Map ${inp}.map" ; fi
if [ $synth = true ]; then
S="-D FOR_SYNTHESIS" ;
c_start="${include}"/syn_start
else
S="-U FOR_SYNTHESIS" ;
c_start="${include}"/start
fi
(mips-gcc -O${level} $warn -DcMIPS -mno-gpopt -I"${include}" \
-S ${src} $S -o ${asm} || exit 1) && \
......
......@@ -25,6 +25,14 @@
extern void exit(int);
extern void cmips_delay(int);
extern void delay_us(int);
extern void delay_ms(int);
extern void enableInterr(void);
extern void disableInterr(void);
extern void print(int);
extern void to_stdout(char c);
extern int from_stdin(void);
......@@ -33,27 +41,22 @@ extern void writeInt(int);
extern void writeClose(void);
extern int readInt(int*);
extern void dumpRAM(void);
extern int print_sp(void);
extern void cmips_delay(int);
extern void delay_us(int);
extern void delay_ms(int);
extern char *memcpy(char*, const char*, int);
extern char *memset(char*, const int, int);
// external counter (peripheral)
extern void startCounter(int, int);
extern void stopCounter(void);
extern int readCounter(void);
extern void enableInterr(void);
extern void disableInterr(void);
// internal counter, CP0 register COUNT
extern int startCount(void);
extern int stopCount(void);
extern int readCount(void);
extern char *memcpy(char*, const char*, int);
extern char *memset(char*, const int, int);
// LCD display (Macnica board)
extern void LCDinit(void);
extern int LCDprobe(void);
extern int LCDset(int);
......@@ -63,13 +66,15 @@ extern void LCDtopLine(void);
extern void LCDbotLine(void);
extern void LCDgotoxy(int, int);
extern void LCDputc(char);
extern void LCDprint(unsigned int);
extern void DSP7SEGput(int MSD, int MSdot, int lsd, int lsdot);
// 7-segment display and keyboard (Macnica board)
extern void DSP7SEGput(int, int, int, int);
extern int KBDget(void);
extern int SWget(void);
// struct to access the system statistics "peripheral"
// struct to access the cache system statistics "peripheral"
typedef struct sStats {
int dc_ref; // data cache references
int dc_rd_hit; // data cache read-hits
......
......@@ -350,6 +350,33 @@ void LCDputc(char c) {
}
}
#define conv(c) ((c<10)?((c)+0x30):((c)+('a'-10)))
// write an integer to the display
void LCDprint(unsigned int n) {
int k;
k = (n >>28);
LCDput( conv(k) );
k = (n<< 4)>>28;
LCDput( conv(k) );
k = (n<< 8)>>28;
LCDput( conv(k) );
k = (n<<12)>>28;
LCDput( conv(k) );
k = (n<<16)>>28;
LCDput( conv(k) );
k = (n<<20)>>28;
LCDput( conv(k) );
k = (n<<24)>>28;
LCDput( conv(k) );
k = (n<<28)>>28;
LCDput( conv(k) );
}
//-----------------------------------------------------------------------
......
# interrupt handlers
.include "cMIPS.s"
.text
.set noreorder # do not reorder instructions
.set noat # do not use register $1 as $at
.align 2
.set M_StatusIEn,0x0000e011 # STATUS.intEn=1, user mode, EXL=0
#================================================================
# interrupt handler for external counter attached to IP5=HW3
# for extCounter address see vhdl/packageMemory.vhd
#
# counter set to interrupt 4 times per second (12,500,000*20ns)
#
.bss
.align 2
.global _counter_val # accumulate number of interrupts
_counter_val: .space 4
_counter_saves: .space 8*4 # area to save up to 8 registers
# _counter_saves[0]=$a0, [1]=$a1, [2]=$a2, [3]=$a3, ...
.set HW_counter_value,(0xc0000000 | 0x00bebc20) # 12,500,000
.text
.set noreorder
.global extCounter
.ent extCounter
extCounter:
lui $k0, %hi(HW_counter_addr)
ori $k0, $k0, %lo(HW_counter_addr)
sw $zero, 0($k0) # Reset counter, remove interrupt request
#----------------------------------
# if you change this handler, then save additional registers
# lui $k1, %hi(_counter_saves)
# ori $k1, $k1, %lo(_counter_saves)
# sw $a0, 0*4($k1)
# sw $a1, 1*4($k1)
#----------------------------------
lui $k1, %hi(HW_counter_value)
ori $k1, $k1, %lo(HW_counter_value)
sw $k1, 0($k0) # Reload counter so it starts again
lui $k0, %hi(_counter_val) # Increment interrupt event counter
ori $k0, $k0, %lo(_counter_val)
lw $k1,0($k0)
nop
addiu $k1,$k1,1
sw $k1,0($k0)
#----------------------------------
# if you changed this handler, then restore those same registers
# lui $k1, %hi(_counter_saves)
# ori $k1, $k1, %lo(_counter_saves)
# lw $a0, 0*4($k1)
# lw $a1, 1*4($k1)
#----------------------------------
mfc0 $k0, c0_status # Read STATUS register
ori $k0, $k0, M_StatusIEn # but do not modify its contents
mtc0 $k0, c0_status # except for re-enabling interrupts
ehb
eret # Return from interrupt
.end extCounter
#----------------------------------------------------------------
#================================================================
# interrupt handler for UART attached to IP6=HW4
# for UART's address see vhdl/packageMemory.vhd
#
.global Ud, _uart_buff
.bss
.align 2
Ud:
rx_hd: .space 4
rx_tl: .space 4
rx_q: .space 16 # reception queue and pointers
tx_hd: .space 4
tx_tl: .space 4
tx_q: .space 16 # transmission queue and pointers
nrx: .space 4 # characters in RX_queue
ntx: .space 4 # spaces left in TX_queue
_uart_buff: .space 16*4 # up to 16 registers to be saved here
.set UART_rx_irq,0x08
.set UART_tx_irq,0x10
.text
.set noreorder
.global UARTinterr
.ent UARTinterr
# _uart_buff[0]=UARTstatus, [1]=UARTcontrol, [2]=data_inp, [3]=new,
# [4]=$ra, [5]=$a0, [6]=$a1, [7]=$a2, [8]=$a3
UARTinterr:
lui $k0, %hi(_uart_buff) # get buffer's address
ori $k0, $k0, %lo(_uart_buff)
sw $a0, 5*4($k0) # save registers $a0,$a1, others?
sw $a1, 6*4($k0)
lui $a0, %hi(HW_uart_addr)# get device's address
ori $a0, $a0, %lo(HW_uart_addr)
lw $k1, 0($a0) # Read status, remove interrupt request
nop
sw $k1, 0*4($k0) # and save UART status to memory
#----------------------------------
# while you are developing the complete handler,
# uncomment the line below and comment out lines up to UARTret
# .include "../tests/handlerUART.s"
#----------------------------------
andi $a1, $k1, UART_rx_irq # Is this reception?
beq $a1, $zero, UARTret # no, ignore it and return
nop
# handle reception
lw $a1, 4($a0) # Read data from device
nop # and store it to UART's buffer
sw $a1, 2*4($k0) # and return from interrupt
addiu $a1, $zero, 1
sw $a1, 3*4($k0) # Signal new arrival
UARTret:
lw $a1, 6*4($k0) # restore registers $a0,$a1, others?
lw $a0, 5*4($k0)
mfc0 $k0, c0_status # Read STATUS register
ori $k0, $k0, M_StatusIEn # but do not modify its contents
mtc0 $k0, c0_status # except for re-enabling interrupts
eret # Return from interrupt
.end UARTinterr
#----------------------------------------------------------------
#================================================================
# handler for COUNT-COMPARE registers -- IP7=HW5
.text
.set noreorder
.equ num_cycles, 64
.global countCompare
.ent countCompare
countCompare:
mfc0 $k1,c0_count # read COUNT
addiu $k1,$k1,num_cycles # set next interrupt in so many ticks
mtc0 $k1,c0_compare # write to COMPARE to clear IRQ
mfc0 $k0, c0_status # Read STATUS register
ori $k0, $k0, M_StatusIEn # but do not modify its contents
mtc0 $k0, c0_status # except for re-enabling interrupts
eret # Return from interrupt
.end countCompare
#----------------------------------------------------------------
#================================================================
# startCount enables the COUNT register, returns new CAUSE
# CAUSE.dc <= 0 to enable counting
#----------------------------------------------------------------
.text
.set noreorder
.global startCount
.ent startCount
startCount:
mfc0 $v0, c0_cause
lui $v1, 0xf7ff
ori $v1, $v1, 0xffff
and $v0, $v0, $v1
jr $ra
mtc0 $v0, c0_cause
.end startCount
#----------------------------------------------------------------
#================================================================
# stopCount disables the COUNT register, returns new CAUSE
# CAUSE.dc <= 1 to disable counting
#----------------------------------------------------------------
.text
.set noreorder
.global stopCount
.ent stopCount
stopCount:
mfc0 $v0, c0_cause
lui $v1, 0x0800
or $v0, $v0, $v1
jr $ra
mtc0 $v0, c0_cause
.end stopCount
#----------------------------------------------------------------
#================================================================
# readCount returns the value of the COUNT register
#----------------------------------------------------------------
.text
.set noreorder
.global readCount
.ent readCount
readCount:
mfc0 $v0, c0_count
jr $ra
nop
.end readCount
#----------------------------------------------------------------
#================================================================
# functions to enable and disable interrupts, both return STATUS
.text
.set noreorder
.global enableInterr,disableInterr
.ent enableInterr
enableInterr:
mfc0 $v0, c0_status # Read STATUS register
ori $v0, $v0, 1 # and enable interrupts
mtc0 $v0, c0_status
nop
jr $ra # return updated STATUS
nop
.end enableInterr
.ent disableInterr
disableInterr:
mfc0 $v0, c0_status # Read STATUS register
addiu $v1, $zero, -2 # and disable interrupts
and $v0, $v0, $v1 # -2 = 0xffff.fffe
mtc0 $v0, c0_status
nop
jr $ra # return updated STATUS
nop
.end disableInterr
#----------------------------------------------------------------
#================================================================
## TLB handlers
## page table entry is { EntryLo0, int0, EntryLo1, int1 }
## int{0,1} is
## { fill_31..6, Modified_5, Used_4, Writable_3, eXecutable_2,
## Status_10 },
## Status: 00=unmapped, 01=mapped, 10=secondary_storage, 11=panic
#================================================================
#================================================================
# handle TLB Modified exception -- store to page with bit dirty=0
#
# (a) fix TLB entry, by setting dirty=1 ;
# (b) check permissions in PT entry and (maybe) kill the process
# OR mark PT entry as Used and Modified, then
# update TLB entry.
#
.global _excp_saves
.global _excp_0180ret
.global handle_Mod
.set noreorder
.ent handle_Mod
handle_Mod: # EntryHi points to offending TLB entry
tlbp # what is the offender's index?
lui $k1, %hi(_excp_saves)
ori $k1, $k1, %lo(_excp_saves)
sw $a0, 9*4($k1) # save registers
sw $a1, 10*4($k1)
sw $a2, 11*4($k1)
mfc0 $a0, c0_badvaddr
andi $a0, $a0, 0x1000 # check if even or odd page
beq $a0, $zero, M_even
mfc0 $a0, c0_context
M_odd: addi $a2, $a0, 12 # address for odd entry
mfc0 $k0, c0_entrylo1
ori $k0, $k0, 0x0004 # mark TLB entry as dirty/writable
j M_test
mtc0 $k0, c0_entrylo1
M_even: addi $a2, $a0, 4 # address for even entry
mfc0 $k0, c0_entrylo0
ori $k0, $k0, 0x0004 # mark TLB entry as dirty/writable
mtc0 $k0, c0_entrylo0
M_test: lw $a1, 0($a2) # read PT[badVAddr]
mfc0 $k0, c0_badvaddr # get faulting address
andi $a0, $a1, 0x0001 # check if page is mapped
beq $a0, $zero, M_seg_fault # no, abort simulation
nop
andi $a0, $a1, 0x0008 # check if page is writable
beq $a0, $zero, M_prot_viol # no, abort simulation
nop
andi $a0, $a1, 0x0002 # check if page is in secondary memory
bne $a0, $zero, M_sec_mem # yes, abort simulation
nop
mfc0 $a0, c0_epc # check if fault is on an instruction
beq $a0, $k0, M_prot_viol # k0 is badVAddr, if so, abort
nop
ori $a1, $a1, 0x0030 # mark PT entry as modified, used
sw $a1, 0($a2)
tlbwi # write entry with dirty bit=1 back to TLB
lw $a0, 9*4($k1) # restore saved registers and return
lw $a1, 10*4($k1)
j _excp_0180ret
lw $a2, 11*4($k1)
M_seg_fault: # print message and abort simulation
la $k1, x_IO_BASE_ADDR
sw $k0, 0($k1)
jal cmips_kmsg
la $k1, 3 # segmentation fault
nop
nop
nop
wait 0x41
M_prot_viol: # print message and abort simulation
la $k1, x_IO_BASE_ADDR
sw $k0, 0($k1)
jal cmips_kmsg
la $k1, 2 # protection violation
nop
nop
nop
wait 0x42
M_sec_mem: # print message and abort simulation
la $k1, x_IO_BASE_ADDR
sw $k0, 0($k1)
jal cmips_kmsg
la $k1, 4 # secondary memory
nop
nop
nop
wait 0x43
.end handle_Mod
#----------------------------------------------------------------
#================================================================
# handle TLB Load exception: double-fault caused by a TLB miss
# to the Page Table -- mapping pointing to PT is not on TLB
#
# (a) fix the fault by (re)loading the mapping into TLB[4];
# (b) check permissions in PT entry and (maybe) kill the process.
#
.global handle_TLBL
.global _PT
.set MIDDLE_RAM, (x_DATA_BASE_ADDR + (x_DATA_MEM_SZ/2))
.ent handle_TLBL
handle_TLBL: # EntryHi points to offending TLB entry
tlbp # probe it to find the offender's index
lui $k1, %hi(_excp_saves)
ori $k1, $k1, %lo(_excp_saves)
sw $a0, 9*4($k1)
sw $a1, 10*4($k1)
sw $a2, 11*4($k1)
mfc0 $a0, c0_badvaddr
la $a1, (_PT + (x_INST_BASE_ADDR >>13)*16)
slt $a2, $a0, $a1 # a2 <- (badVAddr <= PageTable)
bne $a2, $zero, L_chks # fault is not to PageTable
nop
# this is same code as in start.s
# get physical page number for two pages at the bottom of PageTable
la $a0, ( MIDDLE_RAM >>13 )<<13
mtc0 $a0, c0_entryhi # tag for bottom double-page
la $a0, ( (MIDDLE_RAM + 0*4096) >>12 )<<6
ori $a1, $a0, 0b00000000000000000000000000000111 # ccc=0, d,v,g1
mtc0 $a1, c0_entrylo0 # bottom page (even)
la $a0, ( (MIDDLE_RAM + 1*4096) >>12 )<<6
ori $a1, $a0, 0b00000000000000000000000000000111 # ccc=0, d,v,g1
mtc0 $a1, c0_entrylo1 # bottom page + 1 (odd)
# and write it to TLB[4]
li $k0, 4
mtc0 $k0, c0_index
tlbwi
j L_ret # all work done, return
nop
L_chks: andi $a0, $a0, 0x1000 # check if even or odd page
beq $a0, $zero, L_even
mfc0 $a0, c0_context
L_odd: j L_test
addi $a2, $a0, 12 # address for odd intLo1 entry
L_even: addi $a2, $a0, 4 # address for even intLo0 entry
L_test: lw $a1, 0($a2) # get intLo{0,1}
mfc0 $k0, c0_badvaddr # get faulting address for printing
andi $a0, $a1, 0x0001 # check if page is mapped
beq $a0, $zero, M_seg_fault # no, abort simulation
nop
andi $a0, $a1, 0x0002 # check if page is in secondary memory
bne $a0, $zero, M_sec_mem # yes, abort simulation
nop
ori $a1, $a1, 0x0010 # mark PT entry as used
sw $a1, 0($a2)
# if this were handler_TLBS, now is the time to also mark the
# PT entry as Modified
L_ret: lw $a0, 9*4($k1) # nothing else to do, return
lw $a1, 10*4($k1)
j _excp_0180ret
lw $a2, 11*4($k1)
.end handle_TLBL
#----------------------------------------------------------------