URL
https://opencores.org/ocsvn/or1k/or1k/trunk
Subversion Repositories or1k
Compare Revisions
- This comparison shows the changes necessary to convert path
/
- from Rev 1243 to Rev 1244
- ↔ Reverse comparison
Rev 1243 → Rev 1244
/trunk/or1ksim/debug/debug_unit.c
52,10 → 52,8
/* Current watchpoint state */ |
unsigned long watchpoints = 0; |
|
static int calculate_watchpoints (); |
static int calculate_watchpoints(DebugUnitAction action, unsigned long udata); |
|
static int watchpoint[10]; |
|
void set_stall_state(int state) |
{ |
development.riscop &= ~RISCOP_STALL; |
71,6 → 69,11
set_stall_state (0); |
} |
|
void du_clock() |
{ |
watchpoints=0; |
}; |
|
inline int CheckDebugUnit(DebugUnitAction action, unsigned long udata) |
{ |
/* Do not stop, if we have debug module disabled or during reset */ |
81,133 → 84,126
if(action == DebugInstructionFetch && testsprbits (SPR_DMR1, SPR_DMR1_ST)) |
return 1; |
|
// is any watchpoint enabled to generate a break or count? If not, ignore |
if(mfspr(SPR_DMR2) & (SPR_DMR2_WGB|SPR_DMR2_AWTC)) |
return calculate_watchpoints(action, udata); |
|
return 0; |
/* TODO: Enable matchpoints |
switch(action) |
{ |
case DebugInstructionFetch: condition = DCR_CT_InsnAddress; break; |
case DebugLoadAddress: condition = DCR_CT_LoadAddress; break; |
case DebugStoreAddress: condition = DCR_CT_StoreAddress; break; |
case DebugLoadData: condition = DCR_CT_LoadData; break; |
case DebugStoreData: condition = DCR_CT_StoreData; break; |
} |
|
return calculate_watchpoints(); */ |
} |
|
/* Checks whether we should stall the RISC or cause an exception */ |
static int calculate_watchpoints() |
static int calculate_watchpoints(DebugUnitAction action, unsigned long udata) |
{ |
int breakpoint = 0; |
int i, bit; |
|
/* Hopefully this loop would be unrolled run at max. speed */ |
for(i = 0, bit = 1; i < 11; i++, bit <<= 1) { |
int chain1, chain2; |
int match = 0; |
int DCR_hit = 0; |
|
/* Calculate first 8 matchpoints, result is put into DCR_hit */ |
if (i < 8) { |
unsigned long dcr = mfspr (SPR_DCR(i)); |
/* Whether this matchpoint is enabled, calculate conditions */ |
if ((dcr & SPR_DCR_DP) && (dcr & SPR_DCR_CT != SPR_DCR_CT_DISABLED)) { |
/* Get one operand */ |
unsigned long op1; |
unsigned long op2 = mfspr (SPR_DVR(i)); |
switch (dcr & SPR_DCR_CT) { |
case SPR_DCR_CT_IFEA: op1 = runtime.cpu.ifea; break; |
case SPR_DCR_CT_LEA: op1 = runtime.cpu.lea; break; |
case SPR_DCR_CT_SEA: op1 = runtime.cpu.sea; break; |
case SPR_DCR_CT_LD: op1 = runtime.cpu.ld; break; |
case SPR_DCR_CT_SD: op1 = runtime.cpu.sd; break; |
case SPR_DCR_CT_LSEA: op1 = runtime.cpu.lsea; break; |
} |
/* Perform signed comparison? */ |
if (dcr & SPR_DCR_SC) { |
long sop1 = op1, sop2 = op2; /* Convert to signed */ |
switch(dcr & SPR_DCR_CC) { |
case SPR_DCR_CC_MASKED: DCR_hit = sop1 & sop2; break; |
case SPR_DCR_CC_EQUAL: DCR_hit = sop1 == sop2; break; |
case SPR_DCR_CC_NEQUAL: DCR_hit = sop1 != sop2; break; |
case SPR_DCR_CC_LESS: DCR_hit = sop1 < sop2; break; |
case SPR_DCR_CC_LESSE: DCR_hit = sop1 <= sop2; break; |
case SPR_DCR_CC_GREAT: DCR_hit = sop1 > sop2; break; |
case SPR_DCR_CC_GREATE: DCR_hit = sop1 >= sop2; break; |
} |
} else { |
switch(dcr & SPR_DCR_CC) { |
case SPR_DCR_CC_MASKED: DCR_hit = op1 & op2; break; |
case SPR_DCR_CC_EQUAL: DCR_hit = op1 == op2; break; |
case SPR_DCR_CC_NEQUAL: DCR_hit = op1 != op2; break; |
case SPR_DCR_CC_LESS: DCR_hit = op1 < op2; break; |
case SPR_DCR_CC_LESSE: DCR_hit = op1 <= op2; break; |
case SPR_DCR_CC_GREAT: DCR_hit = op1 > op2; break; |
case SPR_DCR_CC_GREATE: DCR_hit = op1 >= op2; break; |
} |
} |
} |
} |
int breakpoint = 0; |
int i, bit; |
|
/* Chain matchpoints */ |
switch(i) { |
case 0: |
chain1 = chain2 = DCR_hit; |
break; |
case 8: |
chain1 = getsprbits (SPR_DWCR0, SPR_DWCR_COUNT) == getsprbits (SPR_DWCR0, SPR_DWCR_MATCH); |
chain2 = watchpoints & (1 << 7); |
break; |
case 9: |
chain1 = getsprbits (SPR_DWCR1, SPR_DWCR_COUNT) == getsprbits (SPR_DWCR1, SPR_DWCR_MATCH); |
chain2 = watchpoints & (1 << 8); |
break; |
case 10: |
/* TODO: External watchpoint - not yet handled! */ |
/* Hopefully this loop would be unrolled run at max. speed */ |
for(i = 0, bit = 1; i < 11; i++, bit <<= 1) { |
int chain1, chain2; |
int match = 0; |
int DCR_hit = 0; |
|
/* Calculate first 8 matchpoints, result is put into DCR_hit */ |
if (i < 8) { |
unsigned long dcr = mfspr (SPR_DCR(i)); |
unsigned long dcr_ct=dcr&SPR_DCR_CT; // the CT field alone |
/* Is this matchpoint a propos for the current action? */ |
if ( ((dcr & SPR_DCR_DP) && dcr_ct) &&// DVR/DCP pair present |
(((action==DebugInstructionFetch) && (dcr_ct == SPR_DCR_CT_IFEA)) |
|| ((action==DebugLoadAddress) && ((dcr_ct == SPR_DCR_CT_LEA) || (dcr_ct == SPR_DCR_CT_LSEA))) |
|| ((action==DebugStoreAddress) && ((dcr_ct == SPR_DCR_CT_SEA) || (dcr_ct == SPR_DCR_CT_LSEA))) |
|| ((action==DebugLoadData) && ((dcr_ct == SPR_DCR_CT_LD) || (dcr_ct == SPR_DCR_CT_LSD))) |
|| ((action==DebugStoreData) && ((dcr_ct == SPR_DCR_CT_SD) || (dcr_ct == SPR_DCR_CT_LSD)))) ) |
{ |
unsigned long op1=udata; |
unsigned long op2 = mfspr (SPR_DVR(i)); |
/* Perform signed comparison? */ |
if (dcr & SPR_DCR_SC) { |
long sop1 = op1, sop2 = op2; /* Convert to signed */ |
switch(dcr & SPR_DCR_CC) { |
case SPR_DCR_CC_MASKED: DCR_hit = sop1 & sop2; break; |
case SPR_DCR_CC_EQUAL: DCR_hit = sop1 == sop2; break; |
case SPR_DCR_CC_NEQUAL: DCR_hit = sop1 != sop2; break; |
case SPR_DCR_CC_LESS: DCR_hit = sop1 < sop2; break; |
case SPR_DCR_CC_LESSE: DCR_hit = sop1 <= sop2; break; |
case SPR_DCR_CC_GREAT: DCR_hit = sop1 > sop2; break; |
case SPR_DCR_CC_GREATE: DCR_hit = sop1 >= sop2; break; |
} |
} else { |
switch(dcr & SPR_DCR_CC) { |
case SPR_DCR_CC_MASKED: DCR_hit = op1 & op2; break; |
case SPR_DCR_CC_EQUAL: DCR_hit = op1 == op2; break; |
case SPR_DCR_CC_NEQUAL: DCR_hit = op1 != op2; break; |
case SPR_DCR_CC_LESS: DCR_hit = op1 < op2; break; |
case SPR_DCR_CC_LESSE: DCR_hit = op1 <= op2; break; |
case SPR_DCR_CC_GREAT: DCR_hit = op1 > op2; break; |
case SPR_DCR_CC_GREATE: DCR_hit = op1 >= op2; break; |
} |
} |
} |
} |
|
/* Chain matchpoints */ |
switch(i) |
{ |
case 0: |
chain1 = chain2 = DCR_hit; |
break; |
case 8: |
chain1 = getsprbits (SPR_DWCR0, SPR_DWCR_COUNT) == getsprbits (SPR_DWCR0, SPR_DWCR_MATCH); |
chain2 = watchpoints & (1 << 7); |
break; |
case 9: |
chain1 = getsprbits (SPR_DWCR1, SPR_DWCR_COUNT) == getsprbits (SPR_DWCR1, SPR_DWCR_MATCH); |
chain2 = watchpoints & (1 << 8); |
break; |
case 10: |
/* TODO: External watchpoint - not yet handled! */ |
#if 0 |
chain1 = external_watchpoint; |
chain2 = watchpoints & (1 << 9); |
chain1 = external_watchpoint; |
chain2 = watchpoints & (1 << 9); |
#else |
chain1 = chain2 = 0; |
chain1 = chain2 = 0; |
#endif |
break; |
default: |
chain1 = DCR_hit; |
chain2 = watchpoints & (bit >> 1); |
break; |
} |
break; |
default: |
chain1 = DCR_hit; |
chain2 = watchpoints & (bit >> 1); |
break; |
} |
|
switch(getsprbits (SPR_DMR1, SPR_DMR1_CW0 << i)) { |
case 0: match = chain1; break; |
case 1: match = chain1 && chain2; break; |
case 2: match = chain1 || chain2; break; |
default: |
break; |
} |
switch(getsprbits (SPR_DMR1, SPR_DMR1_CW0 << i)) { |
case 0: match = chain1; break; |
case 1: match = chain1 && chain2; break; |
case 2: match = chain1 || chain2; break; |
default: |
break; |
} |
|
if(match & !(watchpoints & bit)) { |
int counter = (getsprbits (SPR_DMR2, SPR_DMR2_AWTC) & bit) ? 1 : 0; |
int enabled = counter ? getsprbits (SPR_DMR2, SPR_DMR2_WCE1) : getsprbits (SPR_DMR2, SPR_DMR2_WCE0); |
// Increment counters & generate counter break |
if(match) |
{ |
if(!(watchpoints & bit)) // watchpoint did not appear before in this clock cycle |
{ |
int counter = (getsprbits (SPR_DMR2, SPR_DMR2_AWTC) & bit) ? 1 : 0; |
int enabled = counter ? getsprbits (SPR_DMR2, SPR_DMR2_WCE1) : getsprbits (SPR_DMR2, SPR_DMR2_WCE0); |
if(enabled) |
setsprbits (SPR_DWCR0 + counter, SPR_DWCR_COUNT, getsprbits (SPR_DWCR0 + counter, SPR_DWCR_COUNT) + 1); |
watchpoints |= bit; |
} |
|
if(enabled) |
setsprbits (SPR_DWCR0 + counter, SPR_DWCR_COUNT, getsprbits (SPR_DWCR0 + counter, SPR_DWCR_COUNT) + 1); |
|
if(getsprbits (SPR_DMR2, SPR_DMR2_WGB) & bit) |
breakpoint = 1; |
} |
|
watchpoints &= ~bit; |
watchpoints |= bit; |
} |
|
return breakpoint; |
// should this watchpoint generate a breakpoint? |
if(getsprbits (SPR_DMR2, SPR_DMR2_WGB) & bit) breakpoint = 1; |
} |
} |
return breakpoint; |
} |
|
|
static DebugScanChainIDs current_scan_chain = JTAG_CHAIN_GLOBAL; |
|
int DebugGetRegister(unsigned int address, unsigned long* data) |
{ |
int err; |
int err=0; |
#ifdef DEBUG_JTAG |
PRINTF("Debug get register %x\n",address); |
fflush(stdout); |
223,13 → 219,12
break; |
case JTAG_CHAIN_TRACE: |
*data = 0; /* Scan chain not yet implemented */ |
err = 0; |
break; |
case JTAG_CHAIN_DEVELOPMENT: |
err = get_devint_reg(address,data); |
break; |
case JTAG_CHAIN_WISHBONE: |
err = GetWishboneMemory(address,data); |
err = debug_get_mem(address,data); |
break; |
} |
#ifdef DEBUG_JTAG |
241,7 → 236,7
|
int DebugSetRegister(unsigned int address,unsigned long data) |
{ |
int err; |
int err=0; |
#ifdef DEBUG_JTAG |
PRINTF("Debug set register %x <- %x\n", address, data); |
fflush(stdout); |
387,22 → 382,14
if(!verify_memoryarea(address)) |
err = JTAG_PROXY_INVALID_ADDRESS; |
else { |
unsigned char t_data[4]; |
int *tmp = (int*)t_data; |
extern char null_str[1]; /* From cpu/common/parse.c */ |
int i; |
|
*tmp = htonl(data); /* We have already converted to host order */ |
|
setsim_mem8(address++, t_data[0]); /* Back to network byte order */ |
setsim_mem8(address++, t_data[1]); |
setsim_mem8(address++, t_data[2]); |
setsim_mem8(address++, t_data[3]); |
// circumvent the read-only check usually done for mem accesses |
// data is in host order, because that's what simmem_write_word needs |
simmem_write_word(address, data); |
} |
return err; |
} |
|
int GetWishboneMemory(unsigned int address,unsigned long *data) |
int debug_get_mem(unsigned int address,unsigned long *data) |
{ |
int err = 0; |
if(!verify_memoryarea(address)) |
409,16 → 396,7
err = JTAG_PROXY_INVALID_ADDRESS; |
else |
{ |
unsigned char t_data[4]; |
int *tmp = (int*)t_data; |
int bp; |
|
t_data[0] = evalsim_mem8(address++); |
t_data[1] = evalsim_mem8(address++); |
t_data[2] = evalsim_mem8(address++); |
t_data[3] = evalsim_mem8(address++); /* Already in network byte order */ |
|
*data = ntohl(*tmp); /* But we assume it is in host order later */ |
*data=simmem_read_word(address); |
} |
debug (2, "MEMREAD (%08x) = %08x\n", address, *data); |
return err; |
/trunk/or1ksim/debug/debug_unit.h
88,6 → 88,9
/* Resets the debug unit */ |
void du_reset(void); |
|
/* do cycle-related initialisation (watchpoints etc.) */ |
void du_clock(); |
|
/* set cpu_stalled flag */ |
void set_stall_state (int state); |
|
/trunk/or1ksim/cpu/or32/execute.c
266,10 → 266,10
} |
break_just_hit = 0; |
} |
runtime.cpu.instructions++; |
|
pc_phy &= ~0x03; |
|
runtime.cpu.instructions++; |
|
/* Fetch instruction. */ |
iqueue[0].insn_addr = pc; |
iqueue[0].insn = eval_insn (pc_phy, &breakpoint); |
/trunk/or1ksim/cpu/or32/insnset.c
39,7 → 39,7
|
temp4 = temp1; |
if (temp4 == temp1) |
mstats.byteadd++; |
or1k_mstats.byteadd++; |
} |
INSTRUCTION (l_addc) { |
signed long temp1, temp2, temp3; |
62,7 → 62,7
|
temp4 = temp1; |
if (temp4 == temp1) |
mstats.byteadd++; |
or1k_mstats.byteadd++; |
} |
INSTRUCTION (l_sw) { |
int old_cyc = 0; |
227,7 → 227,7
if (config.bpb.enabled) { |
int fwd = (eval_operand32(0, &breakpoint) >= pc) ? 1 : 0; |
IFF (config.cpu.dependstats) current->func_unit = it_branch; |
mstats.bf[flag][fwd]++; |
or1k_mstats.bf[flag][fwd]++; |
bpb_update(current->insn_addr, flag); |
} |
if (flag) { |
242,7 → 242,7
if (config.bpb.enabled) { |
int fwd = (eval_operand32(0, &breakpoint) >= pc) ? 1 : 0; |
IFF (config.cpu.dependstats) current->func_unit = it_branch; |
mstats.bnf[!flag][fwd]++; |
or1k_mstats.bnf[!flag][fwd]++; |
bpb_update(current->insn_addr, flag == 0); |
} |
if (flag == 0) { |
/trunk/or1ksim/cpu/or32/generate.c
131,7 → 131,10
int output_function (FILE *fo, const char *func_name, int level) |
{ |
FILE *fi; |
if ((fi = fopen (in_file, "rt")) == NULL) return 1; |
if ((fi = fopen (in_file, "rt")) == NULL) { |
printf("could not open file\n"); |
return 1; |
}; |
while (!feof (fi)) { |
char line[10000], *str = line; |
fgets (str, sizeof (line), fi); |
165,6 → 168,7
} |
SHIFT; |
} while (olevel); |
fclose(fi); |
return 0; |
} |
} |
174,6 → 178,8
SHIFT; fprintf (fo, "%s ();\n", func_name); |
level--; |
SHIFT; fprintf (fo, "}"); |
|
fclose(fi); |
return 0; |
} |
|
270,7 → 276,7
level--; |
SHIFT; fprintf (fo, "}\n"); |
if (write_to_reg) { |
SHIFT; fprintf (fo, "reg[0] = 0; /* Repair in case we changed it */\n", i); |
SHIFT; fprintf (fo, "reg[0] = 0; /* Repair in case we changed it */\n"); |
} |
level--; |
SHIFT; fprintf (fo, "}"); |
343,11 → 349,11
fprintf (fo, "\n"); |
SHIFT; fprintf (fo, "/* Not unique: real mask %08x and current mask %08x differ - do final check */\n", ti[i].insn_mask, cur_mask); |
SHIFT; fprintf (fo, "if ((insn & 0x%08x) == 0x%08x) ", ti[i].insn_mask, ti[i].insn); |
output_call (fo, i, level); |
if (output_call (fo, i, level)) return 1; // Fail |
fprintf (fo, " else "); |
if (output_call (fo, -1, level)) return 1; |
if (output_call (fo, -1, level)) return 1; // Fail |
} else { |
output_call (fo, i, level - 1); |
if (output_call (fo, i, level - 1)) return 1; // Fail |
} |
fprintf (fo, " break;\n"); |
} |
/trunk/or1ksim/cpu/common/stats.c
41,7 → 41,7
struct dstats_entry dstats[DSTATS_LEN]; /* dependency stats */ |
struct sstats_entry sstats[SSTATS_LEN]; /* single stats */ |
struct fstats_entry fstats[FSTATS_LEN]; /* functional units stats */ |
struct mstats_entry mstats = {0}; /* misc units stats */ |
struct mstats_entry or1k_mstats = {0}; /* misc units stats */ |
struct cachestats_entry ic_stats = {0}; /* instruction cache stats */ |
struct cachestats_entry dc_stats = {0}; /* data cache stats */ |
struct immustats_entry immu_stats = {0}; /* insn mmu stats */ |
127,7 → 127,7
for (i = 0; i < DSTATS_LEN; i++) |
dstats[i].insn1 = dstats[i].insn2 = -1; |
memset(fstats, 0, sizeof(fstats)); |
memset(&mstats, 0, sizeof(mstats)); |
memset(&or1k_mstats, 0, sizeof(or1k_mstats)); |
memset(&ic_stats, 0, sizeof(ic_stats)); |
memset(&dc_stats, 0, sizeof(dc_stats)); |
memset(&raw_stats, 0, sizeof(raw_stats)); |
142,16 → 142,16
struct branchstat bf; |
struct branchstat bnf; |
long bf_all, bnf_all; |
bf.taken = mstats.bf[1][0] + mstats.bf[1][1]; |
bf.nottaken = mstats.bf[0][0] + mstats.bf[0][1]; |
bf.forward = mstats.bf[0][1] + mstats.bf[1][1]; |
bf.backward = mstats.bf[0][0] + mstats.bf[1][0]; |
bf.taken = or1k_mstats.bf[1][0] + or1k_mstats.bf[1][1]; |
bf.nottaken = or1k_mstats.bf[0][0] + or1k_mstats.bf[0][1]; |
bf.forward = or1k_mstats.bf[0][1] + or1k_mstats.bf[1][1]; |
bf.backward = or1k_mstats.bf[0][0] + or1k_mstats.bf[1][0]; |
bf_all = bf.forward + bf.backward; |
|
bnf.taken = mstats.bnf[1][0] + mstats.bf[1][1]; |
bnf.nottaken = mstats.bnf[0][0] + mstats.bf[0][1]; |
bnf.forward = mstats.bnf[0][1] + mstats.bf[1][1]; |
bnf.backward = mstats.bnf[0][0] + mstats.bf[1][0]; |
bnf.taken = or1k_mstats.bnf[1][0] + or1k_mstats.bf[1][1]; |
bnf.nottaken = or1k_mstats.bnf[0][0] + or1k_mstats.bf[0][1]; |
bnf.forward = or1k_mstats.bnf[0][1] + or1k_mstats.bf[1][1]; |
bnf.backward = or1k_mstats.bnf[0][0] + or1k_mstats.bf[1][0]; |
bnf_all = bnf.forward + bnf.backward; |
|
PRINTF("bnf: %d (%d%%) taken,", bf.taken, (bf.taken * 100) / SD(bf_all)); |
164,15 → 164,15
PRINTF(" %d (%d%%) backward\n", bnf.backward, (bnf.backward * 100) / SD(bnf_all)); |
|
PRINTF("StaticBP bnf(%s): correct %d%%\n", config.bpb.sbp_bnf_fwd ? "forward" : "backward", |
(mstats.bnf[0][config.bpb.sbp_bnf_fwd] * 100) / SD(bnf_all)); |
(or1k_mstats.bnf[0][config.bpb.sbp_bnf_fwd] * 100) / SD(bnf_all)); |
PRINTF("StaticBP bf(%s): correct %d%%\n", config.bpb.sbp_bf_fwd ? "forward" : "backward", |
(mstats.bnf[1][config.bpb.sbp_bf_fwd] * 100) / SD(bf_all)); |
PRINTF("BPB: hit %d (correct %d%%), miss %d\n", mstats.bpb.hit, (mstats.bpb.correct * 100) / SD(mstats.bpb.hit), mstats.bpb.miss); |
(or1k_mstats.bnf[1][config.bpb.sbp_bf_fwd] * 100) / SD(bf_all)); |
PRINTF("BPB: hit %d (correct %d%%), miss %d\n", or1k_mstats.bpb.hit, (or1k_mstats.bpb.correct * 100) / SD(or1k_mstats.bpb.hit), or1k_mstats.bpb.miss); |
} else |
PRINTF("BPB simulation disabled. Enable it to see BPB analysis\n"); |
|
if (config.bpb.btic) { |
PRINTF("BTIC: hit %d(%d%%), miss %d\n", mstats.btic.hit, (mstats.btic.hit * 100) / SD(mstats.btic.hit + mstats.btic.miss), mstats.btic.miss); |
PRINTF("BTIC: hit %d(%d%%), miss %d\n", or1k_mstats.btic.hit, (or1k_mstats.btic.hit * 100) / SD(or1k_mstats.btic.hit + or1k_mstats.btic.miss), or1k_mstats.btic.miss); |
} else |
PRINTF("BTIC simulation disabled. Enabled it to see BTIC analysis\n"); |
|
286,6 → 286,6
break; |
} |
#if 0 |
PRINTF("Byte ADD: %d instructions\n", mstats.byteadd); |
PRINTF("Byte ADD: %d instructions\n", or1k_mstats.byteadd); |
#endif |
} |
/trunk/or1ksim/cpu/common/coff.h
1,3 → 1,7
#if HAVE_CONFIG_H |
#include <config.h> |
#endif |
|
/* This file is derived from the GAS 2.1.4 assembler control file. |
The GAS product is under the GNU Public License, version 2 or later. |
As such, this file is also under that license. |
20,23 → 24,34
* and the values loaded from the character positions. It also makes it |
* nice to have it "endian" independent. |
*/ |
|
#if !defined(WORDS_BIGENDIAN) |
/* Load a short int from the following tables with little-endian formats */ |
#define COFF_SHORT_L(ps) ((short)(((unsigned short)((unsigned char)ps[1])<<8)|\ |
#define COFF_SHORT_L SWAP_ENDIAN_SHORT |
/* Load a long int from the following tables with little-endian formats */ |
#define COFF_LONG_L SWAP_ENDIAN_LONG |
/* Load a short int from the following tables with big-endian formats */ |
#define COFF_SHORT_H KEEP_ENDIAN_SHORT |
/* Load a long int from the following tables with big-endian formats */ |
#define COFF_LONG_H KEEP_ENDIAN_LONG |
#else |
#define COFF_SHORT_L KEEP_ENDIAN_SHORT |
#define COFF_LONG_L KEEP_ENDIAN_LONG |
#define COFF_SHORT_H SWAP_ENDIAN_SHORT |
#define COFF_LONG_H SWAP_ENDIAN_LONG |
#endif |
|
#define SWAP_ENDIAN_SHORT(ps) ((short)(((unsigned short)((unsigned char)ps[1])<<8)|\ |
((unsigned short)((unsigned char)ps[0])))) |
|
/* Load a long int from the following tables with little-endian formats */ |
#define COFF_LONG_L(ps) (((long)(((unsigned long)((unsigned char)ps[3])<<24) |\ |
#define SWAP_ENDIAN_LONG(ps) (((long)(((unsigned long)((unsigned char)ps[3])<<24) |\ |
((unsigned long)((unsigned char)ps[2])<<16) |\ |
((unsigned long)((unsigned char)ps[1])<<8) |\ |
((unsigned long)((unsigned char)ps[0]))))) |
|
/* Load a short int from the following tables with big-endian formats */ |
#define COFF_SHORT_H(ps) ((short)(((unsigned short)((unsigned char)ps[0])<<8)|\ |
#define KEEP_ENDIAN_SHORT(ps) ((short)(((unsigned short)((unsigned char)ps[0])<<8)|\ |
((unsigned short)((unsigned char)ps[1])))) |
|
/* Load a long int from the following tables with big-endian formats */ |
#define COFF_LONG_H(ps) (((long)(((unsigned long)((unsigned char)ps[0])<<24) |\ |
#define KEEP_ENDIAN_LONG(ps) (((long)(((unsigned long)((unsigned char)ps[0])<<24) |\ |
((unsigned long)((unsigned char)ps[1])<<16) |\ |
((unsigned long)((unsigned char)ps[2])<<8) |\ |
((unsigned long)((unsigned char)ps[3]))))) |
/trunk/or1ksim/cpu/common/abstract.c
362,8 → 362,14
mprofile (memaddr, MPROF_32 | MPROF_FETCH); |
// memaddr = simulate_ic_mmu_fetch(memaddr); |
cur_vadd = pc; |
if (config.debug.enabled) |
*breakpoint += CheckDebugUnit(DebugLoadAddress,memaddr); /* 28/05/01 CZ */ |
|
// I think this does not belong into eval_insn() 2004-01-30 HP |
// if (config.debug.enabled) |
// *breakpoint += CheckDebugUnit(DebugLoadAddress,memaddr); /* 28/05/01 CZ */ |
|
// We could place the CheckDebugUnit(DebugInstructionFetch) here, but it is currently done |
// in decode_execute_wrapper, so I leave it like this. 2004-01-30 HP |
|
if (config.ic.enabled) |
temp = ic_simulate_fetch(memaddr); |
else { |
375,8 → 381,9
} |
} |
|
if (config.debug.enabled) |
*breakpoint += CheckDebugUnit(DebugLoadData,temp); /* MM170901 */ |
// I think this does not belong into eval_insn() 2004-01-30 HP |
// if (config.debug.enabled) |
// *breakpoint += CheckDebugUnit(DebugLoadData,temp); /* MM170901 */ |
return temp; |
} |
|
/trunk/or1ksim/cpu/common/stats.h
91,7 → 91,8
int range[RAW_RANGE]; |
}; /* RAW hazard stats */ |
|
extern struct mstats_entry mstats; |
/* Renamed mstats to or1k_mstats because Mac OS X has a lib function called mstats */ |
extern struct mstats_entry or1k_mstats; |
extern struct sstats_entry sstats[SSTATS_LEN]; |
extern struct dstats_entry dstats[DSTATS_LEN]; |
extern struct fstats_entry fstats[FSTATS_LEN]; |
/trunk/or1ksim/cpu/common/elf.h
1,6 → 1,14
#ifndef _LINUX_ELF_H |
#define _LINUX_ELF_H |
|
#if HAVE_CONFIG_H |
#include <config.h> |
#endif |
|
#ifdef WORDS_BIGENDIAN |
#define ELF_SHORT_H |
#define ELF_LONG_H |
#else |
/* Load a short int from the following tables with big-endian formats */ |
#define ELF_SHORT_H(ps) ((((unsigned short)(ps) >> 8) & 0xff) |\ |
(((unsigned short)(ps) << 8) & 0xff00)) |
10,6 → 18,7
(((unsigned long)(ps) >> 8) & 0xff00)|\ |
(((unsigned long)(ps) << 8) & 0xff0000)|\ |
(((unsigned long)(ps) << 24) & 0xff000000)) |
#endif |
|
typedef unsigned long Elf32_Addr; |
typedef unsigned short Elf32_Half; |
/trunk/or1ksim/cpu/or1k/spr_defs.h
267,12 → 267,12
|
/* Bit results with SPR_DCR_CC mask */ |
#define SPR_DCR_CC_MASKED 0x00000000 |
#define SPR_DCR_CC_EQUAL 0x00000001 |
#define SPR_DCR_CC_LESS 0x00000002 |
#define SPR_DCR_CC_LESSE 0x00000003 |
#define SPR_DCR_CC_GREAT 0x00000004 |
#define SPR_DCR_CC_GREATE 0x00000005 |
#define SPR_DCR_CC_NEQUAL 0x00000006 |
#define SPR_DCR_CC_EQUAL 0x00000002 |
#define SPR_DCR_CC_LESS 0x00000004 |
#define SPR_DCR_CC_LESSE 0x00000006 |
#define SPR_DCR_CC_GREAT 0x00000008 |
#define SPR_DCR_CC_GREATE 0x0000000a |
#define SPR_DCR_CC_NEQUAL 0x0000000c |
|
/* Bit results with SPR_DCR_CT mask */ |
#define SPR_DCR_CT_DISABLED 0x00000000 |
282,6 → 282,8
#define SPR_DCR_CT_LD 0x00000080 |
#define SPR_DCR_CT_SD 0x000000a0 |
#define SPR_DCR_CT_LSEA 0x000000c0 |
#define SPR_DCR_CT_LSD 0x000000e0 |
/* SPR_DCR_CT_LSD doesn't seem to be implemented anywhere in or1ksim. 2004-1-30 HP */ |
|
/* |
* Bit definitions for Debug Mode 1 register |
/trunk/or1ksim/extras/Makefile.in
0,0 → 1,332
# Makefile.in generated by automake 1.6.3 from Makefile.am. |
# @configure_input@ |
|
# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002 |
# Free Software Foundation, Inc. |
# This Makefile.in is free software; the Free Software Foundation |
# gives unlimited permission to copy and/or distribute it, |
# with or without modifications, as long as this notice is preserved. |
|
# This program is distributed in the hope that it will be useful, |
# but WITHOUT ANY WARRANTY, to the extent permitted by law; without |
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A |
# PARTICULAR PURPOSE. |
|
@SET_MAKE@ |
|
# Makefile -- Makefile for cpu architecture independent simulation |
# Copyright (C) 1999 Damjan Lampret, lampret@opencores.org |
# |
# This file is part of OpenRISC 1000 Architectural Simulator. |
# |
# This program is free software; you can redistribute it and/or modify |
# it under the terms of the GNU General Public License as published by |
# the Free Software Foundation; either version 2 of the License, or |
# (at your option) any later version. |
# |
# This program is distributed in the hope that it will be useful, |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
# GNU General Public License for more details. |
# |
# You should have received a copy of the GNU General Public License |
# along with this program; if not, write to the Free Software |
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
# |
SHELL = @SHELL@ |
|
srcdir = @srcdir@ |
top_srcdir = @top_srcdir@ |
VPATH = @srcdir@ |
prefix = @prefix@ |
exec_prefix = @exec_prefix@ |
|
bindir = @bindir@ |
sbindir = @sbindir@ |
libexecdir = @libexecdir@ |
datadir = @datadir@ |
sysconfdir = @sysconfdir@ |
sharedstatedir = @sharedstatedir@ |
localstatedir = @localstatedir@ |
libdir = @libdir@ |
infodir = @infodir@ |
mandir = @mandir@ |
includedir = @includedir@ |
oldincludedir = /usr/include |
pkgdatadir = $(datadir)/@PACKAGE@ |
pkglibdir = $(libdir)/@PACKAGE@ |
pkgincludedir = $(includedir)/@PACKAGE@ |
top_builddir = .. |
|
ACLOCAL = @ACLOCAL@ |
AUTOCONF = @AUTOCONF@ |
AUTOMAKE = @AUTOMAKE@ |
AUTOHEADER = @AUTOHEADER@ |
|
am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd |
INSTALL = @INSTALL@ |
INSTALL_PROGRAM = @INSTALL_PROGRAM@ |
INSTALL_DATA = @INSTALL_DATA@ |
install_sh_DATA = $(install_sh) -c -m 644 |
install_sh_PROGRAM = $(install_sh) -c |
install_sh_SCRIPT = $(install_sh) -c |
INSTALL_SCRIPT = @INSTALL_SCRIPT@ |
INSTALL_HEADER = $(INSTALL_DATA) |
transform = @program_transform_name@ |
NORMAL_INSTALL = : |
PRE_INSTALL = : |
POST_INSTALL = : |
NORMAL_UNINSTALL = : |
PRE_UNINSTALL = : |
POST_UNINSTALL = : |
build_alias = @build_alias@ |
build_triplet = @build@ |
host_alias = @host_alias@ |
host_triplet = @host@ |
target_alias = @target_alias@ |
target_triplet = @target@ |
|
EXEEXT = @EXEEXT@ |
OBJEXT = @OBJEXT@ |
PATH_SEPARATOR = @PATH_SEPARATOR@ |
AMTAR = @AMTAR@ |
AR = @AR@ |
ARFLAGS = @ARFLAGS@ |
AWK = @AWK@ |
BUILD_DIR = @BUILD_DIR@ |
CC = @CC@ |
CFLAGS = @CFLAGS@ |
CPU_ARCH = @CPU_ARCH@ |
DEPDIR = @DEPDIR@ |
INCLUDES = @INCLUDES@ |
INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ |
LOCAL_CFLAGS = @LOCAL_CFLAGS@ |
LOCAL_DEFS = @LOCAL_DEFS@ |
LOCAL_LDFLAGS = @LOCAL_LDFLAGS@ |
MAKE_SHELL = @MAKE_SHELL@ |
PACKAGE = @PACKAGE@ |
RANLIB = @RANLIB@ |
STRIP = @STRIP@ |
SUMVERSION = @SUMVERSION@ |
TERMCAP_LIB = @TERMCAP_LIB@ |
VERSION = @VERSION@ |
am__include = @am__include@ |
am__quote = @am__quote@ |
host = @host@ |
host_cpu = @host_cpu@ |
host_os = @host_os@ |
install_sh = @install_sh@ |
|
noinst_LIBRARIES = libextras.a |
libextras_a_SOURCES = extras.c |
subdir = extras |
mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs |
CONFIG_HEADER = $(top_builddir)/config.h |
CONFIG_CLEAN_FILES = |
LIBRARIES = $(noinst_LIBRARIES) |
|
libextras_a_AR = $(AR) cru |
libextras_a_LIBADD = |
am_libextras_a_OBJECTS = extras.$(OBJEXT) |
libextras_a_OBJECTS = $(am_libextras_a_OBJECTS) |
|
DEFS = @DEFS@ |
DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) |
CPPFLAGS = @CPPFLAGS@ |
LDFLAGS = @LDFLAGS@ |
LIBS = @LIBS@ |
depcomp = $(SHELL) $(top_srcdir)/depcomp |
am__depfiles_maybe = depfiles |
@AMDEP_TRUE@DEP_FILES = ./$(DEPDIR)/extras.Po |
COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ |
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) |
CCLD = $(CC) |
LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ |
DIST_SOURCES = $(libextras_a_SOURCES) |
DIST_COMMON = Makefile.am Makefile.in |
SOURCES = $(libextras_a_SOURCES) |
|
all: all-am |
|
.SUFFIXES: |
.SUFFIXES: .c .o .obj |
$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) |
cd $(top_srcdir) && \ |
$(AUTOMAKE) --gnu extras/Makefile |
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status |
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe) |
|
clean-noinstLIBRARIES: |
-test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) |
libextras.a: $(libextras_a_OBJECTS) $(libextras_a_DEPENDENCIES) |
-rm -f libextras.a |
$(libextras_a_AR) libextras.a $(libextras_a_OBJECTS) $(libextras_a_LIBADD) |
$(RANLIB) libextras.a |
|
mostlyclean-compile: |
-rm -f *.$(OBJEXT) core *.core |
|
distclean-compile: |
-rm -f *.tab.c |
|
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/extras.Po@am__quote@ |
|
distclean-depend: |
-rm -rf ./$(DEPDIR) |
|
.c.o: |
@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ |
@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ |
@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ |
$(COMPILE) -c `test -f '$<' || echo '$(srcdir)/'`$< |
|
.c.obj: |
@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ |
@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ |
@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ |
$(COMPILE) -c `cygpath -w $<` |
CCDEPMODE = @CCDEPMODE@ |
uninstall-info-am: |
|
ETAGS = etags |
ETAGSFLAGS = |
|
tags: TAGS |
|
ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) |
list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ |
unique=`for i in $$list; do \ |
if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ |
done | \ |
$(AWK) ' { files[$$0] = 1; } \ |
END { for (i in files) print i; }'`; \ |
mkid -fID $$unique |
|
TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ |
$(TAGS_FILES) $(LISP) |
tags=; \ |
here=`pwd`; \ |
list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ |
unique=`for i in $$list; do \ |
if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ |
done | \ |
$(AWK) ' { files[$$0] = 1; } \ |
END { for (i in files) print i; }'`; \ |
test -z "$(ETAGS_ARGS)$$tags$$unique" \ |
|| $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ |
$$tags $$unique |
|
GTAGS: |
here=`$(am__cd) $(top_builddir) && pwd` \ |
&& cd $(top_srcdir) \ |
&& gtags -i $(GTAGS_ARGS) $$here |
|
distclean-tags: |
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH |
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) |
|
top_distdir = .. |
distdir = $(top_distdir)/$(PACKAGE)-$(VERSION) |
|
distdir: $(DISTFILES) |
@list='$(DISTFILES)'; for file in $$list; do \ |
if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ |
dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ |
if test "$$dir" != "$$file" && test "$$dir" != "."; then \ |
dir="/$$dir"; \ |
$(mkinstalldirs) "$(distdir)$$dir"; \ |
else \ |
dir=''; \ |
fi; \ |
if test -d $$d/$$file; then \ |
if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ |
cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ |
fi; \ |
cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ |
else \ |
test -f $(distdir)/$$file \ |
|| cp -p $$d/$$file $(distdir)/$$file \ |
|| exit 1; \ |
fi; \ |
done |
check-am: all-am |
check: check-am |
all-am: Makefile $(LIBRARIES) |
|
installdirs: |
|
install: install-am |
install-exec: install-exec-am |
install-data: install-data-am |
uninstall: uninstall-am |
|
install-am: all-am |
@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am |
|
installcheck: installcheck-am |
install-strip: |
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ |
INSTALL_STRIP_FLAG=-s \ |
`test -z '$(STRIP)' || \ |
echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install |
mostlyclean-generic: |
|
clean-generic: |
|
distclean-generic: |
-rm -f Makefile $(CONFIG_CLEAN_FILES) |
|
maintainer-clean-generic: |
@echo "This command is intended for maintainers to use" |
@echo "it deletes files that may require special tools to rebuild." |
clean: clean-am |
|
clean-am: clean-generic clean-noinstLIBRARIES mostlyclean-am |
|
distclean: distclean-am |
|
distclean-am: clean-am distclean-compile distclean-depend \ |
distclean-generic distclean-tags |
|
dvi: dvi-am |
|
dvi-am: |
|
info: info-am |
|
info-am: |
|
install-data-am: |
|
install-exec-am: |
|
install-info: install-info-am |
|
install-man: |
|
installcheck-am: |
|
maintainer-clean: maintainer-clean-am |
|
maintainer-clean-am: distclean-am maintainer-clean-generic |
|
mostlyclean: mostlyclean-am |
|
mostlyclean-am: mostlyclean-compile mostlyclean-generic |
|
uninstall-am: uninstall-info-am |
|
.PHONY: GTAGS all all-am check check-am clean clean-generic \ |
clean-noinstLIBRARIES distclean distclean-compile \ |
distclean-depend distclean-generic distclean-tags distdir dvi \ |
dvi-am info info-am install install-am install-data \ |
install-data-am install-exec install-exec-am install-info \ |
install-info-am install-man install-strip installcheck \ |
installcheck-am installdirs maintainer-clean \ |
maintainer-clean-generic mostlyclean mostlyclean-compile \ |
mostlyclean-generic tags uninstall uninstall-am \ |
uninstall-info-am |
|
# Tell versions [3.59,3.63) of GNU make to not export all variables. |
# Otherwise a system limit (for SysV at least) may be exceeded. |
.NOEXPORT: |
/trunk/or1ksim/extras/extras.c
0,0 → 1,23
#include "extras.h" |
|
#include "string.h" |
|
#if !defined(HAVE_STRNDUP) |
|
/* Taken from glibc */ |
char * |
strndup (const char *s, size_t n) |
{ |
size_t len = strlen (s); |
if(len>n) len=n; |
|
char *new = (char *) malloc (len + 1); |
|
if (new == NULL) |
return NULL; |
|
new[len] = '\0'; |
return (char *) memcpy (new, s, len); |
} |
|
#endif |
/trunk/or1ksim/extras/Makefile.am
0,0 → 1,22
# Makefile -- Makefile for cpu architecture independent simulation |
# Copyright (C) 1999 Damjan Lampret, lampret@opencores.org |
# |
# This file is part of OpenRISC 1000 Architectural Simulator. |
# |
# This program is free software; you can redistribute it and/or modify |
# it under the terms of the GNU General Public License as published by |
# the Free Software Foundation; either version 2 of the License, or |
# (at your option) any later version. |
# |
# This program is distributed in the hope that it will be useful, |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
# GNU General Public License for more details. |
# |
# You should have received a copy of the GNU General Public License |
# along with this program; if not, write to the Free Software |
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
# |
|
noinst_LIBRARIES = libextras.a |
libextras_a_SOURCES = extras.c |
/trunk/or1ksim/extras/extras.h
0,0 → 1,10
#ifndef __STRNDUP__ |
#define __STRNDUP__ |
|
#include "stdlib.h" |
|
#if !defined(HAVE_STRNDUP) |
char * strndup (const char *s, size_t n); |
#endif |
|
#endif |
/trunk/or1ksim/cuc/cuc.h
1,311 → 1,309
/* cuc.h -- OpenRISC Custom Unit Compiler, main header file |
* Copyright (C) 2002 Marko Mlinar, markom@opencores.org |
* |
* This file is part of OpenRISC 1000 Architectural Simulator. |
* |
* This program is free software; you can redistribute it and/or modify |
* it under the terms of the GNU General Public License as published by |
* the Free Software Foundation; either version 2 of the License, or |
* (at your option) any later version. |
* |
* This program is distributed in the hope that it will be useful, |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
* GNU General Public License for more details. |
* |
* You should have received a copy of the GNU General Public License |
* along with this program; if not, write to the Free Software |
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ |
|
#ifndef __DATAF_H__ |
#define __DATAF_H__ |
|
/* Maximum number of instructions per function */ |
#define MAX_INSNS 0x10000 |
#define MAX_OPERANDS 4 |
#define MAX_BB 0x1000 |
#define MAX_REGS 34 |
#define FLAG_REG (MAX_REGS - 2) |
#define LRBB_REG (MAX_REGS - 1) |
#define MAX_STACK 0x1000 /* if more, not converted */ |
#define MAX_PREROLL 32 |
#define MAX_UNROLL 32 |
|
#define IT_BRANCH 0x0001 /* Branch instruction */ |
#define IT_INDELAY 0x0002 /* Instruction is in delay slot */ |
#define IT_BBSTART 0x0004 /* BB start marker */ |
#define IT_BBEND 0x0008 /* BB end marker */ |
#define IT_OUTPUT 0x0010 /* this instruction holds final value of the register */ |
#define IT_SIGNED 0x0020 /* Instruction is signed */ |
#define IT_MEMORY 0x0040 /* Instruction does memory access */ |
#define IT_UNUSED 0x0080 /* dead instruction marker */ |
#define IT_FLAG1 0x0100 /* misc flags */ |
#define IT_FLAG2 0x0200 |
#define IT_VOLATILE 0x0400 /* Should not be moved/removed */ |
#define IT_MEMADD 0x0800 /* add before the load -- should not be removed */ |
#define IT_COND 0x1000 /* Conditional */ |
#define IT_LATCHED 0x2000 /* Output of this instruction is latched/registered */ |
#define IT_CUT 0x4000 /* After this instruction register is placed */ |
|
#define OPT_NONE 0x00 |
#define OPT_CONST 0x01 |
#define OPT_REGISTER 0x02 |
#define OPT_REF 0x04 |
#define OPT_JUMP 0x08 /* Jump to an instruction index */ |
#define OPT_DEST 0x10 /* This operand is dest */ |
#define OPT_BB 0x20 /* Jumpt to BB */ |
#define OPT_LRBB 0x40 /* 0 if we came in from left BB, or 1 otherwise */ |
|
#define MT_WIDTH 0x007 /* These bits hold memory access width in bytes */ |
#define MT_BURST 0x008 /* burst start & end markers */ |
#define MT_BURSTE 0x010 |
#define MT_CALL 0x020 /* This is a call */ |
#define MT_LOAD 0x040 /* This memory access does a read */ |
#define MT_STORE 0x080 /* This memory access does a write */ |
#define MT_SIGNED 0x100 /* Signed memory access */ |
|
#define MO_NONE 0 /* different memory ordering, even if there are dependencies, |
burst can be made, width can change */ |
#define MO_WEAK 1 /* different memory ordering, if there cannot be dependencies, |
burst can be made, width can change */ |
#define MO_STRONG 2 /* Same memory ordering, burst can be made, width can change */ |
#define MO_EXACT 3 /* Exacltly the same memory ordering and widths */ |
|
#define BB_INLOOP 0x01 /* This block is inside a loop */ |
#define BB_OPTIONAL 0x02 |
#define BB_DEAD 0x08 /* This block is unaccessible -> to be removed */ |
|
#define BBID_START MAX_BB /* Start BB pointer */ |
#define BBID_END (MAX_BB + 1) /* End BB pointer */ |
|
/* Various macros to minimize code size */ |
#define REF(bb,i) (((bb) * MAX_INSNS) + (i)) |
#define REF_BB(r) ((r) / MAX_INSNS) |
#define REF_I(r) ((r) % MAX_INSNS) |
#define INSN(ref) bb[REF_BB(ref)].insn[REF_I(ref)] |
|
#ifndef MIN |
# define MIN(x,y) ((x) < (y) ? (x) : (y)) |
#endif |
|
#ifndef MAX |
# define MAX(x,y) ((x) > (y) ? (x) : (y)) |
#endif |
|
#define log(x...) {fprintf (flog, x); fflush (flog); } |
|
#define cucdebug(x,s...) {if ((x) <= cuc_debug) PRINTF (s);} |
|
#define CUC_WIDTH_ITERATIONS 256 |
|
/* Options */ |
/* Whether we are debugging cuc (0-9) */ |
extern int cuc_debug; |
|
/* Temporary registers by software convention */ |
extern const int caller_saved[MAX_REGS]; |
|
typedef struct _dep_list_t { |
unsigned long ref; |
struct _dep_list_t *next; |
} dep_list; |
|
/* Shared list, if marked dead, entry is not used */ |
typedef struct _csm_list { |
int ref; |
int cnt; |
int cmovs; |
double size, osize; |
int cmatch; |
int dead; |
int ninsn; /* Number of associated instructions */ |
struct _csm_list *from; |
struct _csm_list *next; |
} cuc_shared_list; |
|
/* Shared resource item definition */ |
typedef struct { |
int ref; |
int cmatch; |
} cuc_shared_item; |
|
/* Implementation specific timings */ |
typedef struct { |
int b; /* Basic block # this timing is referring to */ |
int preroll; /* How many times was this BB pre/unrolled */ |
int unroll; |
int nshared; |
cuc_shared_item *shared; /* List of shared resources */ |
int new_time; |
double size; |
} cuc_timings; |
|
/* Instructionn entity */ |
typedef struct { |
int type; /* type of the instruction */ |
int index; /* Instruction index */ |
int opt[MAX_OPERANDS]; /* operand types */ |
unsigned long op[MAX_OPERANDS]; /* operand values */ |
dep_list *dep; /* instruction dependencies */ |
unsigned long insn; /* Instruction opcode */ |
char disasm[40]; /* disassembled string */ |
unsigned long max; /* max result value */ |
int tmp; |
} cuc_insn; |
|
/* Basic block entity */ |
typedef struct { |
unsigned long type; /* Type of the bb */ |
int first, last; /* Where this block lies */ |
int prev[2], next[2]; |
int tmp; |
cuc_insn *insn; /* Instructions lie here */ |
int ninsn; /* Number of instructions */ |
int last_used_reg[MAX_REGS]; |
dep_list *mdep; /* Last memory access dependencies */ |
int nmemory; |
int cnt; /* how many times was this block executed */ |
int unrolled; /* how many times has been this block unrolled */ |
|
int ntim; /* Basic block options */ |
cuc_timings *tim; |
int selected_tim; /* Selected option, -1 if none */ |
} cuc_bb; |
|
/* Function entity */ |
typedef struct _cuc_func { |
/* Basic blocks */ |
int num_bb; |
cuc_bb bb[MAX_BB]; |
int saved_regs[MAX_REGS];/* Whether this register was saved */ |
int lur[MAX_REGS]; /* Location of last use */ |
int used_regs[MAX_REGS]; /* Nonzero if it was used */ |
|
/* Schedule of memory instructions */ |
int nmsched; |
int msched[MAX_INSNS]; |
int mtype[MAX_INSNS]; |
|
/* initial bb and their relocations to new block numbers */ |
int num_init_bb; |
int *init_bb_reloc; |
int orig_time; /* time in cyc required for SW implementation */ |
int num_runs; /* Number times this function was run */ |
cuc_timings timings; /* Base timings */ |
unsigned long start_addr; /* Address of first instruction inn function */ |
unsigned long end_addr; /* Address of last instruction inn function */ |
int memory_order; /* Memory order */ |
|
int nfdeps; /* Function dependencies */ |
struct _cuc_func **fdeps; |
|
int tmp; |
} cuc_func; |
|
/* Instructions from function */ |
extern cuc_insn insn[MAX_INSNS]; |
extern int num_insn; |
extern int reloc[MAX_INSNS]; |
extern FILE *flog; |
|
/* returns log2(x) */ |
/* |
int log2 (unsigned long x); |
*/ |
|
/* Loads from file into global array insn */ |
int cuc_load (char *in_fn); |
|
/* Negates conditional instruction */ |
void negate_conditional (cuc_insn *ii); |
|
/* Scans sequence of BBs and set bb[].cnt */ |
void generate_bb_seq (cuc_func *f, char *mp_filename, char *bb_filename); |
|
/* Prints out instructions */ |
void print_insns (int bb, cuc_insn *insn, int size, int verbose); |
|
/* prints out bb string */ |
void print_bb_num (int num); |
|
/* Print out basic blocks */ |
void print_cuc_bb (cuc_func *func, char *s); |
|
/* Duplicates function */ |
cuc_func *dup_func (cuc_func *f); |
|
/* Releases memory allocated by function */ |
void free_func (cuc_func *f); |
|
/* Common subexpression matching -- resource sharing, analysis pass */ |
void csm (cuc_func *f); |
|
/* Common subexpression matching -- resource sharing, generation pass */ |
void csm_gen (cuc_func *f, cuc_func *rf, cuc_shared_item *shared, int nshared); |
|
/* Set the BB limits */ |
void detect_bb (cuc_func *func); |
|
/* Optimize basic blocks */ |
int optimize_bb (cuc_func *func); |
|
/* Search and optimize complex cmov assignments */ |
int optimize_cmovs (cuc_func *func); |
|
/* Optimizes dataflow tree */ |
int optimize_tree (cuc_func *func); |
|
/* Remove nop instructions */ |
int remove_nops (cuc_func *func); |
|
/* Removes dead instruction */ |
int remove_dead (cuc_func *func); |
|
/* Removes trivial register assignments */ |
int remove_trivial_regs (cuc_func *f); |
|
/* Determine inputs and outputs */ |
void set_io (cuc_func *func); |
|
/* Removes BBs marked as dead */ |
int remove_dead_bb (cuc_func *func); |
|
/* Common subexpression elimination */ |
int cse (cuc_func *f); |
|
/* Detect register dependencies */ |
void reg_dep (cuc_func *func); |
|
/* Cuts the tree and marks registers */ |
void mark_cut (cuc_func *f); |
|
/* Unroll loop b times times and return new function. Original |
function is unmodified. */ |
cuc_func *preunroll_loop (cuc_func *func, int b, int preroll, int unroll, char *bb_filename); |
|
/* Clean memory and data dependencies */ |
void clean_deps (cuc_func *func); |
|
/* Schedule memory accesses |
0 - exact; 1 - strong; 2 - weak; 3 - none */ |
int schedule_memory (cuc_func *func, int otype); |
|
/* Generates verilog file out of insn dataflow */ |
void output_verilog (cuc_func *func, char *filename, char *funcname); |
|
/* Recalculates bb[].cnt values, based on generated profile file */ |
void recalc_cnts (cuc_func *f, char *bb_filename); |
|
/* Calculate timings */ |
void analyse_timings (cuc_func *func, cuc_timings *timings); |
|
/* Calculates facts, that are determined by conditionals */ |
void insert_conditional_facts (cuc_func *func); |
|
/* Width optimization -- detect maximum values */ |
void detect_max_values (cuc_func *f); |
|
/* Inserts n nops before insn 'ref' */ |
void insert_insns (cuc_func *f, int ref, int n); |
|
#endif /* __DATAF_H__ */ |
/* cuc.h -- OpenRISC Custom Unit Compiler, main header file |
* Copyright (C) 2002 Marko Mlinar, markom@opencores.org |
* |
* This file is part of OpenRISC 1000 Architectural Simulator. |
* |
* This program is free software; you can redistribute it and/or modify |
* it under the terms of the GNU General Public License as published by |
* the Free Software Foundation; either version 2 of the License, or |
* (at your option) any later version. |
* |
* This program is distributed in the hope that it will be useful, |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
* GNU General Public License for more details. |
* |
* You should have received a copy of the GNU General Public License |
* along with this program; if not, write to the Free Software |
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ |
|
#ifndef __DATAF_H__ |
#define __DATAF_H__ |
|
/* Maximum number of instructions per function */ |
#define MAX_INSNS 0x10000 |
#define MAX_OPERANDS 4 |
#define MAX_BB 0x1000 |
#define MAX_REGS 34 |
#define FLAG_REG (MAX_REGS - 2) |
#define LRBB_REG (MAX_REGS - 1) |
#define MAX_STACK 0x1000 /* if more, not converted */ |
#define MAX_PREROLL 32 |
#define MAX_UNROLL 32 |
|
#define IT_BRANCH 0x0001 /* Branch instruction */ |
#define IT_INDELAY 0x0002 /* Instruction is in delay slot */ |
#define IT_BBSTART 0x0004 /* BB start marker */ |
#define IT_BBEND 0x0008 /* BB end marker */ |
#define IT_OUTPUT 0x0010 /* this instruction holds final value of the register */ |
#define IT_SIGNED 0x0020 /* Instruction is signed */ |
#define IT_MEMORY 0x0040 /* Instruction does memory access */ |
#define IT_UNUSED 0x0080 /* dead instruction marker */ |
#define IT_FLAG1 0x0100 /* misc flags */ |
#define IT_FLAG2 0x0200 |
#define IT_VOLATILE 0x0400 /* Should not be moved/removed */ |
#define IT_MEMADD 0x0800 /* add before the load -- should not be removed */ |
#define IT_COND 0x1000 /* Conditional */ |
#define IT_LATCHED 0x2000 /* Output of this instruction is latched/registered */ |
#define IT_CUT 0x4000 /* After this instruction register is placed */ |
|
#define OPT_NONE 0x00 |
#define OPT_CONST 0x01 |
#define OPT_REGISTER 0x02 |
#define OPT_REF 0x04 |
#define OPT_JUMP 0x08 /* Jump to an instruction index */ |
#define OPT_DEST 0x10 /* This operand is dest */ |
#define OPT_BB 0x20 /* Jumpt to BB */ |
#define OPT_LRBB 0x40 /* 0 if we came in from left BB, or 1 otherwise */ |
|
#define MT_WIDTH 0x007 /* These bits hold memory access width in bytes */ |
#define MT_BURST 0x008 /* burst start & end markers */ |
#define MT_BURSTE 0x010 |
#define MT_CALL 0x020 /* This is a call */ |
#define MT_LOAD 0x040 /* This memory access does a read */ |
#define MT_STORE 0x080 /* This memory access does a write */ |
#define MT_SIGNED 0x100 /* Signed memory access */ |
|
#define MO_NONE 0 /* different memory ordering, even if there are dependencies, |
burst can be made, width can change */ |
#define MO_WEAK 1 /* different memory ordering, if there cannot be dependencies, |
burst can be made, width can change */ |
#define MO_STRONG 2 /* Same memory ordering, burst can be made, width can change */ |
#define MO_EXACT 3 /* Exacltly the same memory ordering and widths */ |
|
#define BB_INLOOP 0x01 /* This block is inside a loop */ |
#define BB_OPTIONAL 0x02 |
#define BB_DEAD 0x08 /* This block is unaccessible -> to be removed */ |
|
#define BBID_START MAX_BB /* Start BB pointer */ |
#define BBID_END (MAX_BB + 1) /* End BB pointer */ |
|
/* Various macros to minimize code size */ |
#define REF(bb,i) (((bb) * MAX_INSNS) + (i)) |
#define REF_BB(r) ((r) / MAX_INSNS) |
#define REF_I(r) ((r) % MAX_INSNS) |
#define INSN(ref) bb[REF_BB(ref)].insn[REF_I(ref)] |
|
#ifndef MIN |
# define MIN(x,y) ((x) < (y) ? (x) : (y)) |
#endif |
|
#ifndef MAX |
# define MAX(x,y) ((x) > (y) ? (x) : (y)) |
#endif |
|
#define log(x...) {fprintf (flog, x); fflush (flog); } |
|
#define cucdebug(x,s...) {if ((x) <= cuc_debug) PRINTF (s);} |
|
#define CUC_WIDTH_ITERATIONS 256 |
|
/* Options */ |
/* Whether we are debugging cuc (0-9) */ |
extern int cuc_debug; |
|
/* Temporary registers by software convention */ |
extern const int caller_saved[MAX_REGS]; |
|
typedef struct _dep_list_t { |
unsigned long ref; |
struct _dep_list_t *next; |
} dep_list; |
|
/* Shared list, if marked dead, entry is not used */ |
typedef struct _csm_list { |
int ref; |
int cnt; |
int cmovs; |
double size, osize; |
int cmatch; |
int dead; |
int ninsn; /* Number of associated instructions */ |
struct _csm_list *from; |
struct _csm_list *next; |
} cuc_shared_list; |
|
/* Shared resource item definition */ |
typedef struct { |
int ref; |
int cmatch; |
} cuc_shared_item; |
|
/* Implementation specific timings */ |
typedef struct { |
int b; /* Basic block # this timing is referring to */ |
int preroll; /* How many times was this BB pre/unrolled */ |
int unroll; |
int nshared; |
cuc_shared_item *shared; /* List of shared resources */ |
int new_time; |
double size; |
} cuc_timings; |
|
/* Instructionn entity */ |
typedef struct { |
int type; /* type of the instruction */ |
int index; /* Instruction index */ |
int opt[MAX_OPERANDS]; /* operand types */ |
unsigned long op[MAX_OPERANDS]; /* operand values */ |
dep_list *dep; /* instruction dependencies */ |
unsigned long insn; /* Instruction opcode */ |
char disasm[40]; /* disassembled string */ |
unsigned long max; /* max result value */ |
int tmp; |
} cuc_insn; |
|
/* Basic block entity */ |
typedef struct { |
unsigned long type; /* Type of the bb */ |
int first, last; /* Where this block lies */ |
int prev[2], next[2]; |
int tmp; |
cuc_insn *insn; /* Instructions lie here */ |
int ninsn; /* Number of instructions */ |
int last_used_reg[MAX_REGS]; |
dep_list *mdep; /* Last memory access dependencies */ |
int nmemory; |
int cnt; /* how many times was this block executed */ |
int unrolled; /* how many times has been this block unrolled */ |
|
int ntim; /* Basic block options */ |
cuc_timings *tim; |
int selected_tim; /* Selected option, -1 if none */ |
} cuc_bb; |
|
/* Function entity */ |
typedef struct _cuc_func { |
/* Basic blocks */ |
int num_bb; |
cuc_bb bb[MAX_BB]; |
int saved_regs[MAX_REGS];/* Whether this register was saved */ |
int lur[MAX_REGS]; /* Location of last use */ |
int used_regs[MAX_REGS]; /* Nonzero if it was used */ |
|
/* Schedule of memory instructions */ |
int nmsched; |
int msched[MAX_INSNS]; |
int mtype[MAX_INSNS]; |
|
/* initial bb and their relocations to new block numbers */ |
int num_init_bb; |
int *init_bb_reloc; |
int orig_time; /* time in cyc required for SW implementation */ |
int num_runs; /* Number times this function was run */ |
cuc_timings timings; /* Base timings */ |
unsigned long start_addr; /* Address of first instruction inn function */ |
unsigned long end_addr; /* Address of last instruction inn function */ |
int memory_order; /* Memory order */ |
|
int nfdeps; /* Function dependencies */ |
struct _cuc_func **fdeps; |
|
int tmp; |
} cuc_func; |
|
/* Instructions from function */ |
extern cuc_insn insn[MAX_INSNS]; |
extern int num_insn; |
extern int reloc[MAX_INSNS]; |
extern FILE *flog; |
|
/* returns log2(x) */ |
int log2_int (unsigned long x); |
|
/* Loads from file into global array insn */ |
int cuc_load (char *in_fn); |
|
/* Negates conditional instruction */ |
void negate_conditional (cuc_insn *ii); |
|
/* Scans sequence of BBs and set bb[].cnt */ |
void generate_bb_seq (cuc_func *f, char *mp_filename, char *bb_filename); |
|
/* Prints out instructions */ |
void print_insns (int bb, cuc_insn *insn, int size, int verbose); |
|
/* prints out bb string */ |
void print_bb_num (int num); |
|
/* Print out basic blocks */ |
void print_cuc_bb (cuc_func *func, char *s); |
|
/* Duplicates function */ |
cuc_func *dup_func (cuc_func *f); |
|
/* Releases memory allocated by function */ |
void free_func (cuc_func *f); |
|
/* Common subexpression matching -- resource sharing, analysis pass */ |
void csm (cuc_func *f); |
|
/* Common subexpression matching -- resource sharing, generation pass */ |
void csm_gen (cuc_func *f, cuc_func *rf, cuc_shared_item *shared, int nshared); |
|
/* Set the BB limits */ |
void detect_bb (cuc_func *func); |
|
/* Optimize basic blocks */ |
int optimize_bb (cuc_func *func); |
|
/* Search and optimize complex cmov assignments */ |
int optimize_cmovs (cuc_func *func); |
|
/* Optimizes dataflow tree */ |
int optimize_tree (cuc_func *func); |
|
/* Remove nop instructions */ |
int remove_nops (cuc_func *func); |
|
/* Removes dead instruction */ |
int remove_dead (cuc_func *func); |
|
/* Removes trivial register assignments */ |
int remove_trivial_regs (cuc_func *f); |
|
/* Determine inputs and outputs */ |
void set_io (cuc_func *func); |
|
/* Removes BBs marked as dead */ |
int remove_dead_bb (cuc_func *func); |
|
/* Common subexpression elimination */ |
int cse (cuc_func *f); |
|
/* Detect register dependencies */ |
void reg_dep (cuc_func *func); |
|
/* Cuts the tree and marks registers */ |
void mark_cut (cuc_func *f); |
|
/* Unroll loop b times times and return new function. Original |
function is unmodified. */ |
cuc_func *preunroll_loop (cuc_func *func, int b, int preroll, int unroll, char *bb_filename); |
|
/* Clean memory and data dependencies */ |
void clean_deps (cuc_func *func); |
|
/* Schedule memory accesses |
0 - exact; 1 - strong; 2 - weak; 3 - none */ |
int schedule_memory (cuc_func *func, int otype); |
|
/* Generates verilog file out of insn dataflow */ |
void output_verilog (cuc_func *func, char *filename, char *funcname); |
|
/* Recalculates bb[].cnt values, based on generated profile file */ |
void recalc_cnts (cuc_func *f, char *bb_filename); |
|
/* Calculate timings */ |
void analyse_timings (cuc_func *func, cuc_timings *timings); |
|
/* Calculates facts, that are determined by conditionals */ |
void insert_conditional_facts (cuc_func *func); |
|
/* Width optimization -- detect maximum values */ |
void detect_max_values (cuc_func *f); |
|
/* Inserts n nops before insn 'ref' */ |
void insert_insns (cuc_func *f, int ref, int n); |
|
#endif /* __DATAF_H__ */ |
/trunk/or1ksim/cuc/verilog.c
1,1026 → 1,1026
/* verilog.c -- OpenRISC Custom Unit Compiler, verilog generator |
* Copyright (C) 2002 Marko Mlinar, markom@opencores.org |
* |
* This file is part of OpenRISC 1000 Architectural Simulator. |
* |
* This program is free software; you can redistribute it and/or modify |
* it under the terms of the GNU General Public License as published by |
* the Free Software Foundation; either version 2 of the License, or |
* (at your option) any later version. |
* |
* This program is distributed in the hope that it will be useful, |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
* GNU General Public License for more details. |
* |
* You should have received a copy of the GNU General Public License |
* along with this program; if not, write to the Free Software |
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ |
|
#include <stdio.h> |
#include <stdlib.h> |
#include <stdarg.h> |
#include <assert.h> |
#include "cuc.h" |
#include "insn.h" |
#include "profiler.h" |
#include "sim-config.h" |
|
/* Shortcut */ |
#define GEN(x...) fprintf (fo, x) |
|
/* Find index of load/store/call */ |
int find_lsc_index (cuc_func *f, int ref) |
{ |
int c = 0; |
int i; |
int load; |
|
if (f->INSN(ref).index == II_CALL) { |
for (i = 0; i < f->nmsched; i++) { |
if (f->msched[i] == ref) break; |
if (f->mtype[i] & MT_CALL) c++; |
} |
} else { |
load = II_IS_LOAD (f->INSN(ref).index); |
for (i = 0; i < f->nmsched; i++) { |
if (f->msched[i] == ref) break; |
if (load && (f->mtype[i] & MT_LOAD) |
|| !load && (f->mtype[i] & MT_STORE)) c++; |
} |
} |
return c; |
} |
|
/* Print out dependencies as verilog expression */ |
void print_deps (FILE *fo, cuc_func *f, int b, dep_list *t, int registered) |
{ |
if (t) { |
int first = 0; |
while (t) { |
if (f->INSN(t->ref).type & IT_MEMORY) { |
GEN ("%s%c_end[%i]", first ? " && " : "", |
II_IS_LOAD (f->INSN(t->ref).index) ? 'l' : 's', find_lsc_index (f, t->ref)); |
} else if (f->INSN(t->ref).index == II_CALL) { |
int x; |
GEN ("%sf_end[%i]", first ? " && " : "", find_lsc_index (f, t->ref)); |
} else { |
PRINTF ("print_deps: err %x\n", t->ref); |
assert (0); |
} |
first = 1; |
t = t->next; |
} |
} else { |
if (registered) GEN ("bb_start_r[%i]", b); |
else GEN ("bb_start[%i]", b); |
} |
} |
|
char *print_op_v (cuc_func *f, char *s, int ref, int j) |
{ |
unsigned long op = f->INSN(ref).op[j]; |
unsigned long opt = f->INSN(ref).opt[j]; |
switch (opt & ~OPT_DEST) { |
case OPT_NONE: assert (0); break; |
case OPT_CONST: if (f->INSN(ref).type & IT_COND && (f->INSN(ref).index == II_CMOV |
|| f->INSN(ref).index == II_ADD)) { |
assert (op == 0 || op == 1); |
sprintf (s, "1'b%x", op); |
} else sprintf (s, "32'h%x", op); |
break; |
case OPT_REGISTER: |
if (opt & OPT_DEST) sprintf (s, "t%x_%x", REF_BB(ref), REF_I(ref)); |
else sprintf (s, "r%i_%c", op, opt & OPT_DEST ? 'o' : 'i'); |
break; |
#if 0 |
case OPT_FREG: assert (opt & OPT_DEST); |
sprintf (s, "fr%i_o", op); |
break; |
#endif |
case OPT_REF: sprintf (s, "t%x_%x", REF_BB(op), REF_I(op)); break; |
} |
return s; |
} |
|
/* Prints out specified instruction */ |
void print_insn_v (FILE *fo, cuc_func *f, int b, int i) |
{ |
cuc_insn *ii = &f->bb[b].insn[i]; |
char *s = known[ii->index].rtl; |
char tmp[200] = ""; |
|
assert (s); |
while (*s) { |
if (*s <= MAX_OPERANDS) { |
char t[30]; |
sprintf (tmp, "%s%s", tmp, print_op_v (f, t, REF(b, i), *s - 1)); |
} else if (*s == '\b') sprintf (tmp, "%s%i", b); |
else sprintf (tmp, "%s%c", tmp, *s); |
s++; |
} |
GEN ("%-40s /* %s */\n", tmp, ii->disasm); |
if (ii->type & IT_MEMORY) { |
int j, nls = find_lsc_index (f, REF (b, i)); |
if (II_IS_LOAD (ii->index)) { |
int nm; |
for (nm = 0; nm < f->nmsched; nm++) if (f->msched[nm] == REF (b, i)) break; |
assert (nm < f->nmsched); |
|
GEN (" if (l_end[%i]) t%x_%x <= #Tp ", nls, b, i); |
switch (f->mtype[nm] & (MT_WIDTH | MT_SIGNED)) { |
case 1: GEN ("l_dat_i & 32'hff;\n"); |
break; |
case 2: GEN ("l_dat_i & 32'hffff;\n"); |
break; |
case 4 | MT_SIGNED: |
case 4: GEN ("l_dat_i;\n"); |
break; |
case 1 | MT_SIGNED: |
GEN ("{24{l_dat_i[7]}, l_dat_i[7:0]};\n"); |
break; |
case 2 | MT_SIGNED: |
GEN ("{16{l_dat_i[15]}, l_dat_i[15:0]};\n"); |
break; |
default: assert (0); |
} |
} |
} else if (ii->index == II_LRBB) { |
GEN (" if (rst) t%x_%x <= #Tp 1'b0;\n", b, i); |
assert (f->bb[b].prev[0] >= 0); |
if (f->bb[b].prev[0] == BBID_START) |
GEN (" else if (bb_start[%i]) t%x_%x <= #Tp start_i;\n", b, b, i); |
else |
GEN (" else if (bb_start[%i]) t%x_%x <= #Tp bb_stb[%i];\n", b, b, i, f->bb[b].prev[0]); |
} else if (ii->index == II_REG) { |
assert (ii->opt[1] == OPT_REF); |
GEN (" if ("); |
if (f->bb[b].mdep) print_deps (fo, f, b, f->bb[b].mdep, 0); |
else GEN ("bb_stb[%i]", b); |
GEN (") t%x_%x <= #Tp t%x_%x;\n", b, i, |
REF_BB (ii->op[1]), REF_I (ii->op[1])); |
} |
} |
|
/* Outputs binary number */ |
static char *bin_str (unsigned long x, int len) |
{ |
static char bx[33]; |
char *s = bx; |
while (len > 0) *s++ = '0' + ((x >> --len) & 1); |
*s = '\0'; |
return bx; |
} |
|
/* Returns index of branch instruction inside a block b */ |
static int branch_index (cuc_bb *bb) |
{ |
int i; |
for (i = bb->ninsn - 1; i >= 0; i--) |
if (bb->insn[i].type & IT_BRANCH) return i; |
return -1; |
} |
|
static void print_turn_off_dep (FILE *fo, cuc_func *f, dep_list *dep) |
{ |
while (dep) { |
assert (f->INSN(dep->ref).type & IT_MEMORY || f->INSN(dep->ref).index == II_CALL); |
GEN (" %c_stb[%i] <= #Tp 1'b0;\n", f->INSN(dep->ref).index == II_CALL ? 'f' |
: II_IS_LOAD (f->INSN(dep->ref).index) ? 'l' : 's', find_lsc_index (f, dep->ref)); |
dep = dep->next; |
} |
} |
|
static int func_index (cuc_func *f, int ref) |
{ |
int i; |
unsigned long addr; |
assert (f->INSN(ref).index == II_CALL && f->INSN(ref).opt[0] & OPT_CONST); |
addr = f->INSN(ref).op[0]; |
for (i = 0; i < f->nfdeps; i++) |
if (f->fdeps[i]->start_addr == addr) return i; |
|
assert (0); |
return -1; |
} |
|
/* Generates verilog file out of insn dataflow */ |
void output_verilog (cuc_func *f, char *filename, char *funcname) |
{ |
FILE *fo; |
int b, i, j; |
int ci = 0, co = 0; |
int nloads = 0, nstores = 0, ncalls = 0; |
char tmp[256]; |
sprintf (tmp, "%s.v", filename); |
|
log ("Generating verilog file \"%s\"\n", tmp); |
PRINTF ("Generating verilog file \"%s\"\n", tmp); |
if ((fo = fopen (tmp, "wt+")) == NULL) { |
fprintf (stderr, "Cannot open '%s'\n", tmp); |
exit (1); |
} |
|
/* output header */ |
GEN ("/* %s -- generated by Custom Unit Compiler\n", tmp); |
GEN (" (C) 2002 Opencores\n"); |
GEN (" function \"%s\"\n", funcname); |
GEN (" at %08x - %08x\n", f->start_addr, f->end_addr); |
GEN (" num BBs %i */\n\n", f->num_bb); |
|
GEN ("`include \"timescale.v\"\n\n"); |
GEN ("module %s (clk, rst,\n", filename); |
GEN (" l_adr_o, l_dat_i, l_req_o,\n"); |
GEN (" l_sel_o, l_linbrst_o, l_rdy_i,\n"); |
GEN (" s_adr_o, s_dat_o, s_req_o,\n"); |
GEN (" s_sel_o, s_linbrst_o, s_rdy_i,\n"); |
|
GEN ("/* inputs */ "); |
for (i = 0; i < MAX_REGS; i++) |
if (f->used_regs[i]) { |
GEN ("r%i_i, ", i); |
ci++; |
} |
if (!ci) GEN ("/* NONE */"); |
|
GEN ("\n/* outputs */ "); |
for (i = 0; i < MAX_REGS; i++) |
if (f->lur[i] >= 0 && !f->saved_regs[i]) { |
GEN ("r%i_o, ", i); |
co++; |
} |
|
if (!co) GEN ("/* NONE */"); |
if (f->nfdeps) { |
GEN ("\n/* f. calls */, fstart_o, %sfend_i, fr11_i, ", |
log2 (f->nfdeps) > 0 ? "fid_o, " : ""); |
for (i = 0; i < 6; i++) GEN ("fr%i_o, ", i + 3); |
} |
GEN ("\n start_i, end_o, busy_o);\n\n"); |
|
GEN ("parameter Tp = 1;\n\n"); |
|
GEN ("input clk, rst;\n"); |
GEN ("input start_i;\t/* Module starts when set to 1 */ \n"); |
GEN ("output end_o;\t/* Set when module finishes, cleared upon start_i == 1 */\n"); |
GEN ("output busy_o;\t/* Set when module should not be interrupted */\n"); |
GEN ("\n/* Bus signals */\n"); |
GEN ("output l_req_o, s_req_o;\n"); |
GEN ("input l_rdy_i, s_rdy_i;\n"); |
GEN ("output [3:0] l_sel_o, s_sel_o;\n"); |
GEN ("output [31:0] l_adr_o, s_adr_o;\n"); |
GEN ("output l_linbrst_o, s_linbrst_o;\n"); |
GEN ("input [31:0] l_dat_i;\n"); |
GEN ("output [31:0] s_dat_o;\n\n"); |
|
GEN ("reg l_req_o, s_req_o;\n"); |
GEN ("reg [31:0] l_adr_o, s_adr_o;\n"); |
GEN ("reg [3:0] l_sel_o, s_sel_o;\n"); |
GEN ("reg [31:0] s_dat_o;\n"); |
GEN ("reg l_linbrst_o, s_linbrst_o;\n"); |
|
if (ci || co) GEN ("\n/* module ports */\n"); |
if (ci) { |
int first = 1; |
GEN ("input [31:0]"); |
for (i = 0; i < MAX_REGS; i++) |
if (f->used_regs[i]) { |
GEN ("%sr%i_i", first ? " " : ", ", i); |
first = 0; |
} |
GEN (";\n"); |
} |
|
if (co) { |
int first = 1; |
GEN ("output [31:0]"); |
for (i = 0; i < MAX_REGS; i++) |
if (f->lur[i] >= 0 && !f->saved_regs[i]) { |
GEN ("%sr%i_o", first ? " " : ", ", i); |
first = 0; |
} |
GEN (";\n"); |
} |
|
if (f->nfdeps) { |
GEN ("\n/* Function calls */\n"); |
GEN ("output [31:0] fr3_o"); |
for (i = 1; i < 6; i++) GEN (", fr%i_o", i + 3); |
GEN (";\n"); |
GEN ("input [31:0] fr11_i;\n"); |
if (log2(f->nfdeps) > 0) GEN ("output [%i:0] fid_o;\n", log2(f->nfdeps)); |
GEN ("output fstart_o;\n"); |
GEN ("input fend_i;\n"); |
} |
|
/* Count loads & stores */ |
for (i = 0; i < f->nmsched; i++) |
if (f->mtype[i] & MT_STORE) nstores++; |
else if (f->mtype[i] & MT_LOAD) nloads++; |
else if (f->mtype[i] & MT_CALL) ncalls++; |
|
/* Output internal registers for loads */ |
if (nloads) { |
int first = 1; |
int num = 0; |
GEN ("\n/* internal registers for loads */\n"); |
for (i = 0; i < f->nmsched; i++) |
if (f->mtype[i] & MT_LOAD) { |
GEN ("%st%x_%x", first ? "reg [31:0] " : ", ", |
REF_BB(f->msched[i]), REF_I(f->msched[i])); |
|
if (num >= 8) { |
GEN (";\n"); |
first = 1; |
num = 0; |
} else { |
first = 0; |
num++; |
} |
} |
if (!first) GEN (";\n"); |
} |
|
/* Internal register for function return value */ |
if (f->nfdeps) { |
GEN ("\n/* Internal register for function return value */\n"); |
GEN ("reg [31:0] fr11_r;\n"); |
} |
|
GEN ("\n/* 'zero or one' hot state machines */\n"); |
if (nloads) GEN ("reg [%i:0] l_stb; /* loads */\n", nloads - 1); |
if (nstores) GEN ("reg [%i:0] s_stb; /* stores */\n", nstores - 1); |
GEN ("reg [%i:0] bb_stb; /* basic blocks */\n", f->num_bb - 1); |
|
{ |
int first = 2; |
int num = 0; |
for (b = 0; b < f->num_bb; b++) |
for (i = 0; i < f->bb[b].ninsn; i++) |
if (f->bb[b].insn[i].type & IT_COND |
&& f->bb[b].insn[i].index != II_REG |
&& f->bb[b].insn[i].index != II_LRBB) { |
if (first == 2) GEN ("\n/* basic block condition wires */\n"); |
GEN ("%st%x_%x", first ? "wire " : ", ", b, i); |
if (num >= 8) { |
GEN (";\n"); |
first = 1; |
num = 0; |
} else { |
first = 0; |
num++; |
} |
} |
if (!first) GEN (";\n"); |
|
GEN ("\n/* forward declaration of normal wires */\n"); |
num = 0; |
first = 1; |
for (b = 0; b < f->num_bb; b++) |
for (i = 0; i < f->bb[b].ninsn; i++) |
if (!(f->bb[b].insn[i].type & (IT_COND | IT_BRANCH)) |
&& f->bb[b].insn[i].index != II_REG |
&& f->bb[b].insn[i].index != II_LRBB) { |
/* Exclude loads */ |
if (f->bb[b].insn[i].type & IT_MEMORY && II_IS_LOAD (f->bb[b].insn[i].index)) continue; |
GEN ("%st%x_%x", first ? "wire [31:0] " : ", ", b, i); |
if (num >= 8) { |
GEN (";\n"); |
first = 1; |
num = 0; |
} else { |
first = 0; |
num++; |
} |
} |
if (!first) GEN (";\n"); |
|
GEN ("\n/* forward declaration registers */\n"); |
num = 0; |
first = 1; |
for (b = 0; b < f->num_bb; b++) |
for (i = 0; i < f->bb[b].ninsn; i++) |
if (f->bb[b].insn[i].index == II_REG |
&& f->bb[b].insn[i].index != II_LRBB) { |
GEN ("%st%x_%x", first ? "reg [31:0] " : ", ", b, i); |
if (num >= 8) { |
GEN (";\n"); |
first = 1; |
num = 0; |
} else { |
first = 0; |
num++; |
} |
} |
if (!first) GEN (";\n"); |
|
num = 0; |
first = 1; |
for (b = 0; b < f->num_bb; b++) |
for (i = 0; i < f->bb[b].ninsn; i++) |
if (f->bb[b].insn[i].index != II_REG |
&& f->bb[b].insn[i].index == II_LRBB) { |
GEN ("%st%x_%x", first ? "reg " : ", ", b, i); |
if (num >= 8) { |
GEN (";\n"); |
first = 1; |
num = 0; |
} else { |
first = 0; |
num++; |
} |
} |
if (!first) GEN (";\n"); |
} |
|
if (nloads || nstores) GEN ("\n/* dependencies */\n"); |
if (nloads) GEN ("wire [%i:0] l_end = l_stb & {%i{l_rdy_i}};\n", |
nloads - 1, nloads); |
if (nstores) GEN ("wire [%i:0] s_end = s_stb & {%i{s_rdy_i}};\n", |
nstores - 1, nstores); |
if (ncalls) GEN ("wire [%i:0] f_end = f_stb & {%i{fend_i}};\n", |
ncalls - 1, ncalls); |
|
GEN ("\n/* last dependency */\n"); |
GEN ("wire end_o = "); |
for (b = 0; b < f->num_bb; b++) { |
for (i = 0; i < 2; i++) if (f->bb[b].next[i] == BBID_END) { |
GEN ("bb_stb[%i]", b); |
if (f->bb[b].mdep) { |
GEN (" && "); |
print_deps (fo, f, b, f->bb[b].mdep, 0); |
} |
/* Is branch to BBID_END conditional? */ |
if (f->bb[b].next[1 - i] >= 0) { |
int bidx = branch_index (&f->bb[b]); |
char t[30]; |
print_op_v (f, t, REF (b, bidx), 1); |
GEN (" && %s%s", i ? "" : "!", t); |
} |
} |
} |
GEN (";\n"); |
GEN ("wire busy_o = |bb_stb;\n"); |
|
|
GEN ("\n/* Basic block triggers */\n"); |
GEN ("wire [%2i:0] bb_start = {\n", f->num_bb - 1); |
for (b = f->num_bb - 1; b >= 0; b--) { |
GEN (" /* bb_start[%2i] */ ", b); |
for (i = 0; i < 2; i++) if (f->bb[b].prev[i] >= 0 && f->bb[b].prev[i] != BBID_START) { |
cuc_bb *prev = &f->bb[f->bb[b].prev[i]]; |
int t; |
if (i) GEN ("\n || "); |
if (prev->mdep) { |
print_deps (fo, f, f->bb[b].prev[i], prev->mdep, 0); |
GEN (" && "); |
} |
GEN ("bb_stb[%i]", f->bb[b].prev[i]); |
if (prev->next[0] >= 0 && prev->next[0] != BBID_END |
&& prev->next[1] >= 0 && prev->next[1] != BBID_END) { |
int bi = REF (f->bb[b].prev[i], branch_index (&f->bb[f->bb[b].prev[i]])); |
int ci; |
assert (bi >= 0); |
ci = f->INSN(bi).op[1]; |
t = prev->next[0] == b; |
GEN (" && "); |
if (f->INSN(bi).opt[1] & OPT_REF) { |
GEN ("%st%x_%x", t ? "" : "!", REF_BB(ci), REF_I(ci)); |
} else { |
cucdebug (5, "%x!%x!%x\n", bi, ci, f->INSN(bi).opt[1]); |
assert (f->INSN(bi).opt[1] & OPT_CONST); |
GEN ("%s%i", t ? "" : "!", ci); |
} |
} |
} else break; |
if (!i) GEN ("start_i"); |
if (b == 0) GEN ("};\n"); |
else GEN (",\n"); |
} |
|
GEN ("\n/* Register the bb_start */\n"); |
GEN ("reg [%2i:0] bb_start_r;\n\n", f->num_bb - 1); |
GEN ("always @(posedge rst or posedge clk)\n"); |
GEN ("begin\n"); |
GEN (" if (rst) bb_start_r <= #Tp %i'b0;\n", f->num_bb); |
GEN (" else if (end_o) bb_start_r <= #Tp %i'b0;\n", f->num_bb); |
GEN (" else bb_start_r <= #Tp bb_start;\n"); |
GEN ("end\n"); |
|
GEN ("\n/* Logic */\n"); |
/* output body */ |
for (b = 0; b < f->num_bb; b++) { |
GEN ("\t\t/* BB%i */\n", b); |
for (i = 0; i < f->bb[b].ninsn; i++) |
print_insn_v (fo, f, b, i); |
GEN ("\n"); |
} |
|
if (co) { |
GEN ("\n/* Outputs */\n"); |
for (i = 0; i < MAX_REGS; i++) |
if (f->lur[i] >= 0 && !f->saved_regs[i]) |
GEN ("assign r%i_o = t%x_%x;\n", i, REF_BB(f->lur[i]), |
REF_I(f->lur[i])); |
} |
|
if (nstores) { |
int cur_store; |
GEN ("\n/* Memory stores */\n"); |
GEN ("always @(s_stb"); |
for (i = 0; i < f->nmsched; i++) |
if (f->mtype[i] & MT_STORE) { |
char t[30]; |
unsigned long opt = f->INSN(f->msched[i]).opt[0]; |
if ((opt & ~OPT_DEST) != OPT_CONST) { |
GEN (" or %s", print_op_v (f, t, f->msched[i], 0)); |
} |
} |
|
cur_store = 0; |
GEN (")\nbegin\n"); |
for (i = 0; i < f->nmsched; i++) if (f->mtype[i] & MT_STORE) { |
char t[30]; |
GEN (" %sif (s_stb[%i]) s_dat_o = %s;\n", cur_store == 0 ? "" : "else ", cur_store, |
print_op_v (f, t, f->msched[i], 0)); |
cur_store++; |
//PRINTF ("msched[%i] = %x (mtype %x) %x\n", i, f->msched[i], f->mtype[i], f->INSN(f->msched[i]).op[0]); |
} |
GEN (" else s_dat_o = 32'hx;\n"); |
GEN ("end\n"); |
} |
|
/* Generate load and store state machine */ |
#if 0 |
GEN ("\n/* Load&store state machine */\n"); |
GEN ("always @(posedge clk or posedge rst)\n"); |
GEN (" if (rst) begin\n"); |
if (nloads) GEN (" l_stb <= #Tp %i'h0;\n", nloads); |
if (nstores) GEN (" s_stb <= #Tp %i'h0;\n", nstores); |
GEN (" end else begin\n"); |
for (i = 0; i < f->nmsched; i++) if (f->mtype[i] & MT_LOAD || f->mtype[i] & MT_STORE) { |
int cur = 0; |
dep_list *dep = f->INSN(f->msched[i]).dep; |
assert (f->INSN(f->msched[i]).opt[1] & (OPT_REF | OPT_REGISTER)); |
GEN (" if ("); |
print_deps (fo, f, REF_BB(f->msched[i]), f->INSN(f->msched[i]).dep, 1); |
GEN (") begin\n"); |
print_turn_off_dep (fo, f, dep); |
GEN (" %c_stb[%i] <= #Tp 1'b1;\n", f->mtype[i] & MT_LOAD ? 'l' : 's', cur++); |
GEN (" end\n"); |
} |
GEN (" if (%c_end[%i]) %c_stb <= #Tp %i'h0;\n", c, cur - 1, c, cur); |
GEN (" end\n"); |
#endif |
|
/* Generate state generator machine */ |
for (j = 0; j < 2; j++) { |
char c; |
char *s; |
|
switch (j) { |
case 0: c = 'l'; s = "Load"; break; |
case 1: c = 's'; s = "Store"; break; |
case 2: c = 'c'; s = "Calls"; break; |
} |
if (j == 0 && nloads |
|| j == 1 && nstores |
|| j == 2 && ncalls) { |
int cur = 0; |
char t[30]; |
|
GEN ("\n/* %s state generator machine */\n", s); |
GEN ("always @("); |
for (i = 0; i < f->nmsched; i++) { |
print_op_v (f, t, f->msched[i], 1); |
GEN ("%s or ", t); |
} |
GEN ("bb_start_r"); |
if (nloads) GEN (" or l_end"); |
if (nstores) GEN (" or s_end"); |
GEN (")\n"); |
GEN ("begin\n "); |
cucdebug (1, "%s\n", s); |
for (i = 0; i < f->nmsched; i++) |
if (j == 0 && f->mtype[i] & MT_LOAD |
|| j == 1 && f->mtype[i] & MT_STORE |
|| j == 2 && f->mtype[i] & MT_CALL) { |
dep_list *dep = f->INSN(f->msched[i]).dep; |
cucdebug (1, "msched[%i] = %x (mtype %x)\n", i, f->msched[i], f->mtype[i]); |
assert (f->INSN(f->msched[i]).opt[1] & (OPT_REF | OPT_REGISTER)); |
GEN ("if ("); |
print_deps (fo, f, REF_BB(f->msched[i]), f->INSN(f->msched[i]).dep, 1); |
GEN (") begin\n"); |
GEN (" %c_req_o = 1'b1;\n", c); |
GEN (" %c_sel_o[3:0] = 4'b", c); |
switch (f->mtype[i] & MT_WIDTH) { |
case 1: GEN ("0001 << (%s & 32'h3);\n", |
print_op_v (f, t, f->msched[i], 1)); break; |
case 2: GEN ("0011 << ((%s & 32'h1) << 1);\n", |
print_op_v (f, t, f->msched[i], 1)); break; |
case 4: GEN ("1111;\n"); break; |
default: assert (0); |
} |
GEN (" %c_linbrst_o = 1'b%i;\n", c, |
(f->mtype[i] & MT_BURST) && !(f->mtype[i] & MT_BURSTE) ? 1 : 0); |
GEN (" %c_adr_o = t%x_%x & ~32'h3;\n", c, |
REF_BB(f->INSN(f->msched[i]).op[1]), REF_I(f->INSN(f->msched[i]).op[1])); |
GEN (" end else "); |
} |
GEN ("if (%c_end[%i]) begin\n", c, cur - 1); |
GEN (" %c_req_o = 1'b0;\n", c); |
GEN (" %c_sel_o[3:0] = 4'bx;\n", c); |
GEN (" %c_linbrst_o = 1'b0;\n", c); |
GEN (" %c_adr_o = 32'hx;\n", c); |
GEN (" end else begin\n"); |
GEN (" %c_req_o = 1'b0;\n", c); |
GEN (" %c_sel_o[3:0] = 4'bx;\n", c); |
GEN (" %c_linbrst_o = 1'b0;\n", c); |
GEN (" %c_adr_o = 32'hx;\n", c); |
GEN (" end\n"); |
GEN ("end\n"); |
} |
} |
|
if (ncalls) { |
int cur_call = 0; |
GEN ("\n/* Function calls state machine */\n"); |
GEN ("always @(posedge clk or posedge rst)\n"); |
GEN ("begin\n"); |
GEN (" if (rst) begin\n"); |
GEN (" f_stb <= #Tp %i'h0;\n", nstores); |
for (i = 0; i < 6; i++) GEN (" fr%i_o <= #Tp 32'h0;\n", i + 3); |
if (log2(ncalls)) GEN (" fid_o <= #Tp %i'h0;\n", log2 (f->nfdeps)); |
GEN (" fstart_o <= #Tp 1'b0;\n"); |
//GEN (" f11_r <= #Tp 32'h0;\n"); |
GEN (" end else begin\n"); |
cucdebug (1, "calls \n"); |
for (i = 0; i < f->nmsched; i++) if (f->mtype[i] & MT_CALL) { |
char t[30]; |
dep_list *dep = f->INSN(f->msched[i]).dep; |
cucdebug (1, "msched[%i] = %x (mtype %x)\n", i, f->msched[i], f->mtype[i]); |
assert (f->INSN(f->msched[i]).opt[1] & (OPT_REF | OPT_REGISTER)); |
GEN (" if ("); |
print_deps (fo, f, REF_BB(f->msched[i]), f->INSN(f->msched[i]).dep, 1); |
GEN (") begin\n"); |
print_turn_off_dep (fo, f, dep); |
GEN (" f_stb[%i] <= #Tp 1'b1;\n", cur_call++); |
GEN (" fstart_o <= #Tp 1'b1;\n"); |
if (log2 (f->nfdeps)) |
GEN (" fid_o <= #Tp %i'h%x;\n", log2 (f->nfdeps), func_index (f, f->msched[i])); |
|
for (j = 0; j < 6; j++) |
GEN (" fr%i_o <= #Tp t%x_%x;\n", j + 3, |
REF_BB (f->msched[i]), REF_I (f->msched[i]) - 6 + i); |
GEN (" end\n"); |
} |
GEN (" if (f_end[%i]) begin\n", ncalls - 1); |
GEN (" f_stb <= #Tp %i'h0;\n", ncalls); |
GEN (" f_start_o <= #Tp 1'b0;\n"); |
GEN (" end\n"); |
GEN (" end\n"); |
GEN ("end\n"); |
} |
|
GEN ("\n/* Basic blocks state machine */\n"); |
GEN ("always @(posedge clk or posedge rst)\n"); |
GEN ("begin\n"); |
GEN (" if (rst) bb_stb <= #Tp %i'h%x;\n", f->num_bb, 0); |
GEN (" else if (end_o) begin\n"); |
GEN (" bb_stb <= #Tp %i'h%x;\n", f->num_bb, 0); |
for (i = 0; i < f->num_bb; i++) { |
GEN (" end else if (bb_start[%i]) begin\n", i); |
GEN (" bb_stb <= #Tp %i'h%x;\n", f->num_bb, 1 << i); |
} |
GEN (" end else if (end_o) begin\n"); |
GEN (" bb_stb <= #Tp %i'h%x;\n", f->num_bb, 0); |
GEN (" end\n"); |
GEN ("end\n"); |
|
/* output footer */ |
GEN ("\nendmodule\n"); |
|
fclose (fo); |
} |
|
void generate_main (int nfuncs, cuc_func **f, char *filename) |
{ |
FILE *fo; |
int i, j, nrf, first; |
char tmp[256]; |
int ncallees[MAX_FUNCS]; |
int nl[MAX_FUNCS], ns[MAX_FUNCS]; |
int maxncallees = 0; |
sprintf (tmp, "%s_top.v", filename); |
|
for (i = 0, nrf = 0; i < nfuncs; i++) { |
nl[i] = ns[i] = 0; |
ncallees[i] = 0; |
if (f[i]) { |
f[i]->tmp = nrf++; |
for (j = 0; j < f[i]->nmsched; j++) |
if (f[i]->mtype[j] & MT_LOAD) nl[i]++; |
else if (f[i]->mtype[j] & MT_STORE) ns[i]++; |
for (j = 0; j < f[i]->nfdeps; j++) |
ncallees[f[i]->fdeps[j]->tmp]++; |
} |
} |
if (!nrf) return; |
|
for (i = 0; i < nrf; i++) |
if (maxncallees < ncallees[i]) maxncallees = ncallees[i]; |
|
log ("Generating verilog file \"%s\"\n", tmp); |
PRINTF ("Generating verilog file \"%s\"\n", tmp); |
if ((fo = fopen (tmp, "wt+")) == NULL) { |
fprintf (stderr, "Cannot open '%s'\n", tmp); |
exit (1); |
} |
|
/* output header */ |
GEN ("/* %s -- generated by Custom Unit Compiler\n", tmp); |
GEN (" (C) 2002 Opencores */\n\n"); |
GEN ("/* Includes %i functions:", nrf); |
for (i = 0; i < nfuncs; i++) if (f[i]) |
GEN ("\n%s", prof_func[i].name); |
GEN (" */\n\n"); |
|
GEN ("`include \"timescale.v\"\n\n"); |
GEN ("module %s (clk, rst,\n", filename); |
GEN (" /* Load and store master Wishbone ports */\n"); |
GEN (" l_adr_o, l_dat_i, l_cyc_o, l_stb_o,\n"); |
GEN (" l_sel_o, l_linbrst_o, l_rdy_i, l_we_o,\n"); |
GEN (" s_adr_o, s_dat_o, s_cyc_o, s_stb_o,\n"); |
GEN (" s_sel_o, s_linbrst_o, s_rdy_i, s_we_o,\n\n"); |
GEN (" /* cuc interface */\n"); |
GEN (" cuc_stb_i, cuc_adr_i, cuc_dat_i, cuc_dat_o, cuc_we_i, cuc_rdy_o);\n\n"); |
|
GEN ("parameter Tp = 1;\n"); |
GEN ("\n/* module ports */\n"); |
GEN ("input clk, rst, cuc_stb_i, cuc_we_i;\n"); |
GEN ("input l_rdy_i, s_rdy_i;\n"); |
GEN ("output l_cyc_o, l_stb_o, l_we_o, l_linbrst_o;\n"); |
GEN ("reg l_cyc_o, l_stb_o, l_we_o, l_linbrst_o;\n"); |
GEN ("output s_cyc_o, s_stb_o, s_we_o, s_linbrst_o;\n"); |
GEN ("reg s_cyc_o, s_stb_o, s_we_o, s_linbrst_o;\n"); |
GEN ("output cuc_rdy_o; /* Not registered ! */\n"); |
GEN ("output [3:0] l_sel_o, s_sel_o;\n"); |
GEN ("reg [3:0] l_sel_o, s_sel_o;\n"); |
GEN ("output [31:0] l_adr_o, s_adr_o, s_dat_o, cuc_dat_o;\n"); |
GEN ("reg [31:0] l_adr_o, s_adr_o, s_dat_o, cuc_dat_o;\n"); |
GEN ("input [15:0] cuc_adr_i;\n"); |
GEN ("input [31:0] l_dat_i, cuc_dat_i;\n\n"); |
|
GEN ("wire [%2i:0] i_we, i_re, i_finish, i_selected, i_first_reg;\n", nrf - 1); |
GEN ("wire [%2i:0] i_bidok, i_start_bid, i_start_bidok, main_start, main_end;\n", nrf - 1); |
GEN ("wire [%2i:0] i_start, i_end, i_start_block, i_busy;\n", nrf - 1); |
GEN ("wire [%2i:0] i_l_req, i_s_req;\n", nrf - 1); |
GEN ("reg [%2i:0] i_go_bsy, main_start_r;\n", nrf - 1); |
|
GEN ("assign i_selected = {\n"); |
for (i = 0; i < nrf; i++) |
GEN (" cuc_adr_i[15:6] == %i%s\n", i, i < nrf - 1 ? "," : "};"); |
|
GEN ("assign i_first_reg = {\n"); |
for (i = 0; i < nfuncs; i++) if (f[i]) { |
for (j = 0; j <= MAX_REGS; j++) if (f[i]->used_regs[j]) break; |
GEN (" cuc_adr_i[5:0] == %i%s\n", j, f[i]->tmp < nrf - 1 ? "," : "};"); |
} |
|
GEN ("assign i_we = {%i{cuc_stb_i && cuc_we_i}} & i_selected;\n", nrf); |
GEN ("assign i_re = {%i{cuc_stb_i && !cuc_we_i}} & i_selected;\n", nrf); |
|
GEN ("assign i_start = i_go_bsy & {%i{cuc_rdy_o}};\n", nrf); |
GEN ("assign i_start_bidok = {\n"); |
for (i = 0; i < nrf; i++) |
GEN (" i_start_bid[%i] < %i%s\n", i, i, i < nrf - 1 ? "," : "};"); |
GEN ("assign main_start = i_start & i_selected & i_first_reg & i_we;\n"); |
GEN ("assign main_end = {%i{i_end}} & i_selected;\n"); |
|
GEN ("\nalways @(posedge clk or posedge rst)\n"); |
GEN ("begin\n"); |
GEN (" if (rst) i_go_bsy <= #Tp %i'b0;\n", nrf); |
GEN (" else i_go_bsy <= #Tp i_we | ~i_finish & i_go_bsy;\n"); |
GEN ("end\n"); |
|
|
/* Function specific data */ |
for (i = 0; i < nfuncs; i++) if (f[i]) { |
int ci = 0, co = 0; |
int fn = f[i]->tmp; |
GEN ("\n/* Registers for function %s */\n", prof_func[i].name); |
for (j = 0, first = 1; j < MAX_REGS; j++) if (f[i]->used_regs[j]) { |
GEN ("%s i%i_r%ii", first ? "/* inputs */\nreg [31:0]" : ",", fn, j); |
first = 0; |
ci++; |
} |
if (ci) GEN (";\n"); |
|
for (j = 0, first = 1; j < MAX_REGS; j++) |
if (f[i]->lur[j] >= 0 && !f[i]->saved_regs[j]) { |
GEN ("%s i%i_r%io", first ? "/* outputs */\nwire [31:0]" : ",", fn, j); |
first = 0; |
co++; |
} |
if (co) GEN (";\n"); |
GEN ("wire [31:0] i%i_l_adr, i%i_s_adr;\n", fn, fn); |
|
GEN ("always @(posedge clk or posedge rst)\n"); |
GEN (" if (rst) main_start_r <= #Tp %i'b0;\n", nrf); |
GEN (" else main_start_r <= #Tp main_start & i_start_bidok | i_busy | ~i_end & main_start_r;\n"); |
|
if (ci) { |
GEN ("\n/* write register access */\n"); |
GEN ("always @(posedge clk or posedge rst)\n"); |
GEN ("begin\n"); |
GEN (" if (rst) begin\n"); |
for (j = 0; j < MAX_REGS; j++) if (f[i]->used_regs[j]) |
GEN (" i%i_r%ii <= #Tp 32'h0;\n", fn, j); |
GEN (" end else if (!i_go_bsy[%i] && i_we[%i])\n", fn, fn); |
GEN (" case (cuc_adr_i[5:0])\n"); |
for (j = 0; j < MAX_REGS; j++) if (f[i]->used_regs[j]) |
GEN (" %-2i: i%i_r%ii <= #Tp cuc_dat_i;\n", j, fn, j); |
GEN (" endcase\n"); |
GEN ("end\n"); |
} |
|
GEN ("\n"); |
} |
|
/* Generate machine for reading all function registers. Register read can be |
delayed till function completion */ |
{ |
int co; |
GEN ("/* read register access - data */\n"); |
GEN ("always @(posedge clk or posedge rst)\n"); |
GEN (" if (rst) cuc_dat_o <= #Tp 32'h0;\n"); |
GEN (" else if (cuc_stb_i && cuc_we_i) begin\n"); |
GEN (" "); |
|
for (i = 0; i < nfuncs; i++) if (f[i]) { |
co = 0; |
for (j = 0; j < MAX_REGS; j++) |
if (f[i]->lur[j] >= 0 && !f[i]->saved_regs[j]) co++; |
|
GEN ("if (cuc_adr_i[15:6] == %i)", f[i]->tmp); |
if (co) { |
first = 1; |
GEN ("\n case (cuc_adr_i[5:0])\n"); |
for (j = 0; j < MAX_REGS; j++) |
if (f[i]->lur[j] >= 0 && !f[i]->saved_regs[j]) |
GEN (" %-2i: cuc_dat_o <= #Tp i%i_r%io;\n", j, f[i]->tmp, j); |
GEN (" endcase\n"); |
} else { |
GEN (" cuc_dat_o <= #Tp 32'hx;\n"); |
} |
GEN (" else "); |
} |
GEN ("cuc_dat_o <= #Tp 32'hx;\n"); |
GEN (" end else cuc_dat_o <= #Tp 32'hx;\n"); |
|
GEN ("\n/* read register access - acknowledge */\n"); |
GEN ("assign cuc_rdy_o = cuc_stb_i && cuc_we_i && |(i_selected & main_end);\n"); |
} |
|
/* Store/load Wishbone bridge */ |
for (j = 0; j < 2; j++) { |
char t = j ? 's' : 'l'; |
GEN ("\n/* %s Wishbone bridge */\n", j ? "store" : "load"); |
GEN ("reg [%i:0] %cm_sel;\n", log2 (nrf), t); |
GEN ("reg [%i:0] %cm_bid;\n", log2 (nrf), t); |
GEN ("reg %ccyc_ip;\n\n", t); |
GEN ("always @(posedge clk)\n"); |
GEN ("begin\n"); |
GEN (" %c_we_o <= #Tp 1'b%i;\n", t, j); |
GEN (" %c_cyc_o <= #Tp |i_%c_req;\n", t, t); |
GEN (" %c_stb_o <= #Tp |i_%c_req;\n", t, t); |
GEN ("end\n"); |
|
GEN ("\n/* highest bid */\n"); |
GEN ("always @("); |
for (i = 0; i < nrf; i++) GEN ("%si_%c_req", i > 0 ? " or " : "", t); |
GEN (")\n"); |
for (i = 0; i < nrf; i++) GEN (" %sif (i_%c_req) %cm_bid = %i'h%x;\n", |
i ? "else " : "", t, t, log2 (nrf) + 1, i); |
|
GEN ("\n/* selected transfer */\n"); |
GEN ("always @(posedge clk or posedge rst)\n"); |
GEN (" if (rst) %cm_sel <= #Tp %i'h0;\n", t, log2 (nrf) + 1); |
GEN (" else if (%c_rdy_i) %cm_sel <= #Tp %i'h0;\n", t, t, log2 (nrf) + 1); |
GEN (" else if (!%ccyc_ip) %cm_sel <= #Tp %cm_bid;\n", t, t, t); |
|
GEN ("\n/* Cycle */\n"); |
GEN ("\nalways @(posedge clk or posedge rst)\n"); |
GEN (" if (rst) %ccyc_ip <= #Tp 1'b0;\n", t); |
GEN (" else if (%c_rdy_i) %ccyc_ip <= #Tp 1'b0;\n", t, t); |
GEN (" else %ccyc_ip <= #Tp %c_cyc_o;\n", t, t); |
} |
|
GEN ("\n/* Acknowledge */\n"); |
for (i = 0; i < nrf; i++) { |
GEN ("wire i%i_s_rdy = ((sm_bid == %i & !scyc_ip) | sm_sel == %i) & s_rdy_i;\n", i, i, i); |
GEN ("wire i%i_l_rdy = ((lm_bid == %i & !lcyc_ip) | lm_sel == %i) & l_rdy_i;\n", i, i, i); |
} |
|
GEN ("\n/* data, address selects and burst enables */\n"); |
for (i = 0; i < nrf; i++) GEN ("wire [31:0] i%i_s_dat;\n", i); |
for (i = 0; i < nrf; i++) GEN ("wire i%i_s_linbrst, i%i_l_linbrst;\n", i, i); |
for (i = 0; i < nrf; i++) GEN ("wire [3:0] i%i_s_sel, i%i_l_sel;\n", i, i); |
for (i = 0; i < nrf; i++) GEN ("wire [31:0] i%i_l_dat = l_dat_i;\n", i); |
GEN ("\nalways @(posedge clk)\n"); |
GEN ("begin\n"); |
GEN (" s_dat_o <= #Tp "); |
for (i = 0; i < nrf - 1; i++) |
GEN ("\n sm_bid == %i ? i%i_s_dat : ", i, i); |
GEN ("i%i_s_dat;\n", nrf - 1); |
GEN (" s_adr_o <= #Tp "); |
for (i = 0; i < nrf - 1; i++) |
GEN ("\n sm_bid == %i ? i%i_s_adr : ", i, i); |
GEN ("i%i_s_adr;\n", nrf - 1); |
GEN (" s_sel_o <= #Tp "); |
for (i = 0; i < nrf - 1; i++) |
GEN ("\n sm_bid == %i ? i%i_s_sel : ", i, i); |
GEN ("i%i_s_sel;\n", nrf - 1); |
GEN (" s_linbrst_o <= #Tp "); |
for (i = 0; i < nrf - 1; i++) |
GEN ("\n sm_bid == %i ? i%i_s_linbrst : ", i, i); |
GEN ("i%i_s_linbrst;\n", nrf - 1); |
GEN ("end\n\n"); |
|
GEN ("always @(posedge clk)\n"); |
GEN ("begin\n"); |
GEN (" l_adr_o <= #Tp "); |
for (i = 0; i < nrf - 1; i++) |
GEN ("\n lm_bid == %i ? i%i_l_adr : ", i, i); |
GEN ("i%i_l_adr;\n", nrf - 1); |
GEN (" l_sel_o <= #Tp "); |
for (i = 0; i < nrf - 1; i++) |
GEN ("\n lm_bid == %i ? i%i_l_sel : ", i, i); |
GEN ("i%i_l_sel;\n", nrf - 1); |
GEN (" l_linbrst_o <= #Tp "); |
for (i = 0; i < nrf - 1; i++) |
GEN ("\n lm_bid == %i ? i%i_l_linbrst : ", i, i); |
GEN ("i%i_l_linbrst;\n", nrf - 1); |
GEN ("end\n\n"); |
|
/* start/end signals */ |
GEN ("\n\n/* start/end signals */\n"); |
for (i = 0; i < nrf; i++) { |
if (log2 (maxncallees + 1)) |
GEN ("wire [%i:0] i%i_current = i%i_busy ? i%i_current_r : i%i_start_bid;\n", |
log2 (maxncallees + 1), i, i, i, i, i); |
else GEN ("wire i%i_current = 0;\n", i); |
} |
GEN ("\n"); |
|
for (i = 0, j = 0; i < nfuncs; i++) if (f[i]) { |
if (log2 (ncallees[i])) { |
GEN ("reg [%i:0] i%i_start_bid;\n", log2 (ncallees[i]), j); |
GEN ("always @(start%i", f[i]->tmp); |
for (j = 0, first = 1; j < f[i]->nfdeps; j++) |
if (f[i]->fdeps[j]) GEN (", "); |
GEN (")\n"); |
GEN ("begin !!!\n"); //TODO |
GEN (" \n"); |
GEN ("end\n"); |
} |
GEN ("wire i%i_start = main_start[%i];\n", j, j); |
j++; |
} |
GEN ("\n"); |
|
for (i = 0; i < nfuncs; i++) if (f[i]) { |
int nf = f[i]->tmp; |
GEN ("\n%s%s i%i(.clk(clk), .rst(rst),\n", filename, prof_func[i].name, nf); |
GEN (""); |
GEN (" .l_adr_o(i%i_l_adr), .l_dat_i(i%i_l_dat), .l_req_o(i_l_req[%i]),\n", |
nf, nf, nf); |
GEN (" .l_sel_o(i%i_l_sel), .l_linbrst_o(i%i_l_linbrst), .l_rdy_i(i%i_l_rdy),\n", |
nf, nf, nf); |
GEN (" .s_adr_o(i%i_s_adr), .s_dat_o(i%i_s_dat), .s_req_o(i_s_req[%i]),\n", |
nf, nf, nf); |
GEN (" .s_sel_o(i%i_s_sel), .s_linbrst_o(i%i_s_linbrst), .s_rdy_i(i%i_s_rdy),\n", |
nf, nf, nf); |
GEN (" "); |
for (j = 0, first = 1; j < MAX_REGS; j++) if (f[i]->used_regs[j]) |
GEN (".r%i_i(i%i_r%ii), ", j, nf, j), first = 0; |
|
if (first) GEN ("\n "); |
for (j = 0, first = 1; j < MAX_REGS; j++) |
if (f[i]->lur[j] >= 0 && !f[i]->saved_regs[j]) |
GEN (".r%i_o(i%i_r%io), ", j, nf, j), first = 0; |
if (first) GEN ("\n "); |
if (f[i]->nfdeps) { |
GEN (".fstart_o(i_fstart[%i]), .fend_i(i_fend[%i]), .fid_o(i%i_fid),\n", i, i, i), |
GEN (" .fr3_o(i%i_fr3), .fr4_o(i%i_fr4), .fr5_o(i%i_fr5), .fr6_o(i%i_fr6),\n"); |
GEN (" .fr7_o(i%i_fr7), .fr8_o(i%i_fr8), .fr11_i(i%i_fr11i),\n "); |
} |
GEN (".start_i(i_start[%i]), .end_o(i_end[%i]), .busy_o(i_busy[%i]));\n", nf, nf, nf); |
} |
|
/* output footer */ |
GEN ("\nendmodule\n"); |
|
fclose (fo); |
} |
|
/* verilog.c -- OpenRISC Custom Unit Compiler, verilog generator |
* Copyright (C) 2002 Marko Mlinar, markom@opencores.org |
* |
* This file is part of OpenRISC 1000 Architectural Simulator. |
* |
* This program is free software; you can redistribute it and/or modify |
* it under the terms of the GNU General Public License as published by |
* the Free Software Foundation; either version 2 of the License, or |
* (at your option) any later version. |
* |
* This program is distributed in the hope that it will be useful, |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
* GNU General Public License for more details. |
* |
* You should have received a copy of the GNU General Public License |
* along with this program; if not, write to the Free Software |
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ |
|
#include <stdio.h> |
#include <stdlib.h> |
#include <stdarg.h> |
#include <assert.h> |
#include "cuc.h" |
#include "insn.h" |
#include "profiler.h" |
#include "sim-config.h" |
|
/* Shortcut */ |
#define GEN(x...) fprintf (fo, x) |
|
/* Find index of load/store/call */ |
int find_lsc_index (cuc_func *f, int ref) |
{ |
int c = 0; |
int i; |
int load; |
|
if (f->INSN(ref).index == II_CALL) { |
for (i = 0; i < f->nmsched; i++) { |
if (f->msched[i] == ref) break; |
if (f->mtype[i] & MT_CALL) c++; |
} |
} else { |
load = II_IS_LOAD (f->INSN(ref).index); |
for (i = 0; i < f->nmsched; i++) { |
if (f->msched[i] == ref) break; |
if (load && (f->mtype[i] & MT_LOAD) |
|| !load && (f->mtype[i] & MT_STORE)) c++; |
} |
} |
return c; |
} |
|
/* Print out dependencies as verilog expression */ |
void print_deps (FILE *fo, cuc_func *f, int b, dep_list *t, int registered) |
{ |
if (t) { |
int first = 0; |
while (t) { |
if (f->INSN(t->ref).type & IT_MEMORY) { |
GEN ("%s%c_end[%i]", first ? " && " : "", |
II_IS_LOAD (f->INSN(t->ref).index) ? 'l' : 's', find_lsc_index (f, t->ref)); |
} else if (f->INSN(t->ref).index == II_CALL) { |
int x; |
GEN ("%sf_end[%i]", first ? " && " : "", find_lsc_index (f, t->ref)); |
} else { |
PRINTF ("print_deps: err %x\n", t->ref); |
assert (0); |
} |
first = 1; |
t = t->next; |
} |
} else { |
if (registered) GEN ("bb_start_r[%i]", b); |
else GEN ("bb_start[%i]", b); |
} |
} |
|
char *print_op_v (cuc_func *f, char *s, int ref, int j) |
{ |
unsigned long op = f->INSN(ref).op[j]; |
unsigned long opt = f->INSN(ref).opt[j]; |
switch (opt & ~OPT_DEST) { |
case OPT_NONE: assert (0); break; |
case OPT_CONST: if (f->INSN(ref).type & IT_COND && (f->INSN(ref).index == II_CMOV |
|| f->INSN(ref).index == II_ADD)) { |
assert (op == 0 || op == 1); |
sprintf (s, "1'b%x", op); |
} else sprintf (s, "32'h%x", op); |
break; |
case OPT_REGISTER: |
if (opt & OPT_DEST) sprintf (s, "t%x_%x", REF_BB(ref), REF_I(ref)); |
else sprintf (s, "r%i_%c", op, opt & OPT_DEST ? 'o' : 'i'); |
break; |
#if 0 |
case OPT_FREG: assert (opt & OPT_DEST); |
sprintf (s, "fr%i_o", op); |
break; |
#endif |
case OPT_REF: sprintf (s, "t%x_%x", REF_BB(op), REF_I(op)); break; |
} |
return s; |
} |
|
/* Prints out specified instruction */ |
void print_insn_v (FILE *fo, cuc_func *f, int b, int i) |
{ |
cuc_insn *ii = &f->bb[b].insn[i]; |
char *s = known[ii->index].rtl; |
char tmp[200] = ""; |
|
assert (s); |
while (*s) { |
if (*s <= MAX_OPERANDS) { |
char t[30]; |
sprintf (tmp, "%s%s", tmp, print_op_v (f, t, REF(b, i), *s - 1)); |
} else if (*s == '\b') sprintf (tmp, "%s%i", b); |
else sprintf (tmp, "%s%c", tmp, *s); |
s++; |
} |
GEN ("%-40s /* %s */\n", tmp, ii->disasm); |
if (ii->type & IT_MEMORY) { |
int j, nls = find_lsc_index (f, REF (b, i)); |
if (II_IS_LOAD (ii->index)) { |
int nm; |
for (nm = 0; nm < f->nmsched; nm++) if (f->msched[nm] == REF (b, i)) break; |
assert (nm < f->nmsched); |
|
GEN (" if (l_end[%i]) t%x_%x <= #Tp ", nls, b, i); |
switch (f->mtype[nm] & (MT_WIDTH | MT_SIGNED)) { |
case 1: GEN ("l_dat_i & 32'hff;\n"); |
break; |
case 2: GEN ("l_dat_i & 32'hffff;\n"); |
break; |
case 4 | MT_SIGNED: |
case 4: GEN ("l_dat_i;\n"); |
break; |
case 1 | MT_SIGNED: |
GEN ("{24{l_dat_i[7]}, l_dat_i[7:0]};\n"); |
break; |
case 2 | MT_SIGNED: |
GEN ("{16{l_dat_i[15]}, l_dat_i[15:0]};\n"); |
break; |
default: assert (0); |
} |
} |
} else if (ii->index == II_LRBB) { |
GEN (" if (rst) t%x_%x <= #Tp 1'b0;\n", b, i); |
assert (f->bb[b].prev[0] >= 0); |
if (f->bb[b].prev[0] == BBID_START) |
GEN (" else if (bb_start[%i]) t%x_%x <= #Tp start_i;\n", b, b, i); |
else |
GEN (" else if (bb_start[%i]) t%x_%x <= #Tp bb_stb[%i];\n", b, b, i, f->bb[b].prev[0]); |
} else if (ii->index == II_REG) { |
assert (ii->opt[1] == OPT_REF); |
GEN (" if ("); |
if (f->bb[b].mdep) print_deps (fo, f, b, f->bb[b].mdep, 0); |
else GEN ("bb_stb[%i]", b); |
GEN (") t%x_%x <= #Tp t%x_%x;\n", b, i, |
REF_BB (ii->op[1]), REF_I (ii->op[1])); |
} |
} |
|
/* Outputs binary number */ |
static char *bin_str (unsigned long x, int len) |
{ |
static char bx[33]; |
char *s = bx; |
while (len > 0) *s++ = '0' + ((x >> --len) & 1); |
*s = '\0'; |
return bx; |
} |
|
/* Returns index of branch instruction inside a block b */ |
static int branch_index (cuc_bb *bb) |
{ |
int i; |
for (i = bb->ninsn - 1; i >= 0; i--) |
if (bb->insn[i].type & IT_BRANCH) return i; |
return -1; |
} |
|
static void print_turn_off_dep (FILE *fo, cuc_func *f, dep_list *dep) |
{ |
while (dep) { |
assert (f->INSN(dep->ref).type & IT_MEMORY || f->INSN(dep->ref).index == II_CALL); |
GEN (" %c_stb[%i] <= #Tp 1'b0;\n", f->INSN(dep->ref).index == II_CALL ? 'f' |
: II_IS_LOAD (f->INSN(dep->ref).index) ? 'l' : 's', find_lsc_index (f, dep->ref)); |
dep = dep->next; |
} |
} |
|
static int func_index (cuc_func *f, int ref) |
{ |
int i; |
unsigned long addr; |
assert (f->INSN(ref).index == II_CALL && f->INSN(ref).opt[0] & OPT_CONST); |
addr = f->INSN(ref).op[0]; |
for (i = 0; i < f->nfdeps; i++) |
if (f->fdeps[i]->start_addr == addr) return i; |
|
assert (0); |
return -1; |
} |
|
/* Generates verilog file out of insn dataflow */ |
void output_verilog (cuc_func *f, char *filename, char *funcname) |
{ |
FILE *fo; |
int b, i, j; |
int ci = 0, co = 0; |
int nloads = 0, nstores = 0, ncalls = 0; |
char tmp[256]; |
sprintf (tmp, "%s.v", filename); |
|
log ("Generating verilog file \"%s\"\n", tmp); |
PRINTF ("Generating verilog file \"%s\"\n", tmp); |
if ((fo = fopen (tmp, "wt+")) == NULL) { |
fprintf (stderr, "Cannot open '%s'\n", tmp); |
exit (1); |
} |
|
/* output header */ |
GEN ("/* %s -- generated by Custom Unit Compiler\n", tmp); |
GEN (" (C) 2002 Opencores\n"); |
GEN (" function \"%s\"\n", funcname); |
GEN (" at %08x - %08x\n", f->start_addr, f->end_addr); |
GEN (" num BBs %i */\n\n", f->num_bb); |
|
GEN ("`include \"timescale.v\"\n\n"); |
GEN ("module %s (clk, rst,\n", filename); |
GEN (" l_adr_o, l_dat_i, l_req_o,\n"); |
GEN (" l_sel_o, l_linbrst_o, l_rdy_i,\n"); |
GEN (" s_adr_o, s_dat_o, s_req_o,\n"); |
GEN (" s_sel_o, s_linbrst_o, s_rdy_i,\n"); |
|
GEN ("/* inputs */ "); |
for (i = 0; i < MAX_REGS; i++) |
if (f->used_regs[i]) { |
GEN ("r%i_i, ", i); |
ci++; |
} |
if (!ci) GEN ("/* NONE */"); |
|
GEN ("\n/* outputs */ "); |
for (i = 0; i < MAX_REGS; i++) |
if (f->lur[i] >= 0 && !f->saved_regs[i]) { |
GEN ("r%i_o, ", i); |
co++; |
} |
|
if (!co) GEN ("/* NONE */"); |
if (f->nfdeps) { |
GEN ("\n/* f. calls */, fstart_o, %sfend_i, fr11_i, ", |
log2_int (f->nfdeps) > 0 ? "fid_o, " : ""); |
for (i = 0; i < 6; i++) GEN ("fr%i_o, ", i + 3); |
} |
GEN ("\n start_i, end_o, busy_o);\n\n"); |
|
GEN ("parameter Tp = 1;\n\n"); |
|
GEN ("input clk, rst;\n"); |
GEN ("input start_i;\t/* Module starts when set to 1 */ \n"); |
GEN ("output end_o;\t/* Set when module finishes, cleared upon start_i == 1 */\n"); |
GEN ("output busy_o;\t/* Set when module should not be interrupted */\n"); |
GEN ("\n/* Bus signals */\n"); |
GEN ("output l_req_o, s_req_o;\n"); |
GEN ("input l_rdy_i, s_rdy_i;\n"); |
GEN ("output [3:0] l_sel_o, s_sel_o;\n"); |
GEN ("output [31:0] l_adr_o, s_adr_o;\n"); |
GEN ("output l_linbrst_o, s_linbrst_o;\n"); |
GEN ("input [31:0] l_dat_i;\n"); |
GEN ("output [31:0] s_dat_o;\n\n"); |
|
GEN ("reg l_req_o, s_req_o;\n"); |
GEN ("reg [31:0] l_adr_o, s_adr_o;\n"); |
GEN ("reg [3:0] l_sel_o, s_sel_o;\n"); |
GEN ("reg [31:0] s_dat_o;\n"); |
GEN ("reg l_linbrst_o, s_linbrst_o;\n"); |
|
if (ci || co) GEN ("\n/* module ports */\n"); |
if (ci) { |
int first = 1; |
GEN ("input [31:0]"); |
for (i = 0; i < MAX_REGS; i++) |
if (f->used_regs[i]) { |
GEN ("%sr%i_i", first ? " " : ", ", i); |
first = 0; |
} |
GEN (";\n"); |
} |
|
if (co) { |
int first = 1; |
GEN ("output [31:0]"); |
for (i = 0; i < MAX_REGS; i++) |
if (f->lur[i] >= 0 && !f->saved_regs[i]) { |
GEN ("%sr%i_o", first ? " " : ", ", i); |
first = 0; |
} |
GEN (";\n"); |
} |
|
if (f->nfdeps) { |
GEN ("\n/* Function calls */\n"); |
GEN ("output [31:0] fr3_o"); |
for (i = 1; i < 6; i++) GEN (", fr%i_o", i + 3); |
GEN (";\n"); |
GEN ("input [31:0] fr11_i;\n"); |
if (log2_int(f->nfdeps) > 0) GEN ("output [%i:0] fid_o;\n", log2_int(f->nfdeps)); |
GEN ("output fstart_o;\n"); |
GEN ("input fend_i;\n"); |
} |
|
/* Count loads & stores */ |
for (i = 0; i < f->nmsched; i++) |
if (f->mtype[i] & MT_STORE) nstores++; |
else if (f->mtype[i] & MT_LOAD) nloads++; |
else if (f->mtype[i] & MT_CALL) ncalls++; |
|
/* Output internal registers for loads */ |
if (nloads) { |
int first = 1; |
int num = 0; |
GEN ("\n/* internal registers for loads */\n"); |
for (i = 0; i < f->nmsched; i++) |
if (f->mtype[i] & MT_LOAD) { |
GEN ("%st%x_%x", first ? "reg [31:0] " : ", ", |
REF_BB(f->msched[i]), REF_I(f->msched[i])); |
|
if (num >= 8) { |
GEN (";\n"); |
first = 1; |
num = 0; |
} else { |
first = 0; |
num++; |
} |
} |
if (!first) GEN (";\n"); |
} |
|
/* Internal register for function return value */ |
if (f->nfdeps) { |
GEN ("\n/* Internal register for function return value */\n"); |
GEN ("reg [31:0] fr11_r;\n"); |
} |
|
GEN ("\n/* 'zero or one' hot state machines */\n"); |
if (nloads) GEN ("reg [%i:0] l_stb; /* loads */\n", nloads - 1); |
if (nstores) GEN ("reg [%i:0] s_stb; /* stores */\n", nstores - 1); |
GEN ("reg [%i:0] bb_stb; /* basic blocks */\n", f->num_bb - 1); |
|
{ |
int first = 2; |
int num = 0; |
for (b = 0; b < f->num_bb; b++) |
for (i = 0; i < f->bb[b].ninsn; i++) |
if (f->bb[b].insn[i].type & IT_COND |
&& f->bb[b].insn[i].index != II_REG |
&& f->bb[b].insn[i].index != II_LRBB) { |
if (first == 2) GEN ("\n/* basic block condition wires */\n"); |
GEN ("%st%x_%x", first ? "wire " : ", ", b, i); |
if (num >= 8) { |
GEN (";\n"); |
first = 1; |
num = 0; |
} else { |
first = 0; |
num++; |
} |
} |
if (!first) GEN (";\n"); |
|
GEN ("\n/* forward declaration of normal wires */\n"); |
num = 0; |
first = 1; |
for (b = 0; b < f->num_bb; b++) |
for (i = 0; i < f->bb[b].ninsn; i++) |
if (!(f->bb[b].insn[i].type & (IT_COND | IT_BRANCH)) |
&& f->bb[b].insn[i].index != II_REG |
&& f->bb[b].insn[i].index != II_LRBB) { |
/* Exclude loads */ |
if (f->bb[b].insn[i].type & IT_MEMORY && II_IS_LOAD (f->bb[b].insn[i].index)) continue; |
GEN ("%st%x_%x", first ? "wire [31:0] " : ", ", b, i); |
if (num >= 8) { |
GEN (";\n"); |
first = 1; |
num = 0; |
} else { |
first = 0; |
num++; |
} |
} |
if (!first) GEN (";\n"); |
|
GEN ("\n/* forward declaration registers */\n"); |
num = 0; |
first = 1; |
for (b = 0; b < f->num_bb; b++) |
for (i = 0; i < f->bb[b].ninsn; i++) |
if (f->bb[b].insn[i].index == II_REG |
&& f->bb[b].insn[i].index != II_LRBB) { |
GEN ("%st%x_%x", first ? "reg [31:0] " : ", ", b, i); |
if (num >= 8) { |
GEN (";\n"); |
first = 1; |
num = 0; |
} else { |
first = 0; |
num++; |
} |
} |
if (!first) GEN (";\n"); |
|
num = 0; |
first = 1; |
for (b = 0; b < f->num_bb; b++) |
for (i = 0; i < f->bb[b].ninsn; i++) |
if (f->bb[b].insn[i].index != II_REG |
&& f->bb[b].insn[i].index == II_LRBB) { |
GEN ("%st%x_%x", first ? "reg " : ", ", b, i); |
if (num >= 8) { |
GEN (";\n"); |
first = 1; |
num = 0; |
} else { |
first = 0; |
num++; |
} |
} |
if (!first) GEN (";\n"); |
} |
|
if (nloads || nstores) GEN ("\n/* dependencies */\n"); |
if (nloads) GEN ("wire [%i:0] l_end = l_stb & {%i{l_rdy_i}};\n", |
nloads - 1, nloads); |
if (nstores) GEN ("wire [%i:0] s_end = s_stb & {%i{s_rdy_i}};\n", |
nstores - 1, nstores); |
if (ncalls) GEN ("wire [%i:0] f_end = f_stb & {%i{fend_i}};\n", |
ncalls - 1, ncalls); |
|
GEN ("\n/* last dependency */\n"); |
GEN ("wire end_o = "); |
for (b = 0; b < f->num_bb; b++) { |
for (i = 0; i < 2; i++) if (f->bb[b].next[i] == BBID_END) { |
GEN ("bb_stb[%i]", b); |
if (f->bb[b].mdep) { |
GEN (" && "); |
print_deps (fo, f, b, f->bb[b].mdep, 0); |
} |
/* Is branch to BBID_END conditional? */ |
if (f->bb[b].next[1 - i] >= 0) { |
int bidx = branch_index (&f->bb[b]); |
char t[30]; |
print_op_v (f, t, REF (b, bidx), 1); |
GEN (" && %s%s", i ? "" : "!", t); |
} |
} |
} |
GEN (";\n"); |
GEN ("wire busy_o = |bb_stb;\n"); |
|
|
GEN ("\n/* Basic block triggers */\n"); |
GEN ("wire [%2i:0] bb_start = {\n", f->num_bb - 1); |
for (b = f->num_bb - 1; b >= 0; b--) { |
GEN (" /* bb_start[%2i] */ ", b); |
for (i = 0; i < 2; i++) if (f->bb[b].prev[i] >= 0 && f->bb[b].prev[i] != BBID_START) { |
cuc_bb *prev = &f->bb[f->bb[b].prev[i]]; |
int t; |
if (i) GEN ("\n || "); |
if (prev->mdep) { |
print_deps (fo, f, f->bb[b].prev[i], prev->mdep, 0); |
GEN (" && "); |
} |
GEN ("bb_stb[%i]", f->bb[b].prev[i]); |
if (prev->next[0] >= 0 && prev->next[0] != BBID_END |
&& prev->next[1] >= 0 && prev->next[1] != BBID_END) { |
int bi = REF (f->bb[b].prev[i], branch_index (&f->bb[f->bb[b].prev[i]])); |
int ci; |
assert (bi >= 0); |
ci = f->INSN(bi).op[1]; |
t = prev->next[0] == b; |
GEN (" && "); |
if (f->INSN(bi).opt[1] & OPT_REF) { |
GEN ("%st%x_%x", t ? "" : "!", REF_BB(ci), REF_I(ci)); |
} else { |
cucdebug (5, "%x!%x!%x\n", bi, ci, f->INSN(bi).opt[1]); |
assert (f->INSN(bi).opt[1] & OPT_CONST); |
GEN ("%s%i", t ? "" : "!", ci); |
} |
} |
} else break; |
if (!i) GEN ("start_i"); |
if (b == 0) GEN ("};\n"); |
else GEN (",\n"); |
} |
|
GEN ("\n/* Register the bb_start */\n"); |
GEN ("reg [%2i:0] bb_start_r;\n\n", f->num_bb - 1); |
GEN ("always @(posedge rst or posedge clk)\n"); |
GEN ("begin\n"); |
GEN (" if (rst) bb_start_r <= #Tp %i'b0;\n", f->num_bb); |
GEN (" else if (end_o) bb_start_r <= #Tp %i'b0;\n", f->num_bb); |
GEN (" else bb_start_r <= #Tp bb_start;\n"); |
GEN ("end\n"); |
|
GEN ("\n/* Logic */\n"); |
/* output body */ |
for (b = 0; b < f->num_bb; b++) { |
GEN ("\t\t/* BB%i */\n", b); |
for (i = 0; i < f->bb[b].ninsn; i++) |
print_insn_v (fo, f, b, i); |
GEN ("\n"); |
} |
|
if (co) { |
GEN ("\n/* Outputs */\n"); |
for (i = 0; i < MAX_REGS; i++) |
if (f->lur[i] >= 0 && !f->saved_regs[i]) |
GEN ("assign r%i_o = t%x_%x;\n", i, REF_BB(f->lur[i]), |
REF_I(f->lur[i])); |
} |
|
if (nstores) { |
int cur_store; |
GEN ("\n/* Memory stores */\n"); |
GEN ("always @(s_stb"); |
for (i = 0; i < f->nmsched; i++) |
if (f->mtype[i] & MT_STORE) { |
char t[30]; |
unsigned long opt = f->INSN(f->msched[i]).opt[0]; |
if ((opt & ~OPT_DEST) != OPT_CONST) { |
GEN (" or %s", print_op_v (f, t, f->msched[i], 0)); |
} |
} |
|
cur_store = 0; |
GEN (")\nbegin\n"); |
for (i = 0; i < f->nmsched; i++) if (f->mtype[i] & MT_STORE) { |
char t[30]; |
GEN (" %sif (s_stb[%i]) s_dat_o = %s;\n", cur_store == 0 ? "" : "else ", cur_store, |
print_op_v (f, t, f->msched[i], 0)); |
cur_store++; |
//PRINTF ("msched[%i] = %x (mtype %x) %x\n", i, f->msched[i], f->mtype[i], f->INSN(f->msched[i]).op[0]); |
} |
GEN (" else s_dat_o = 32'hx;\n"); |
GEN ("end\n"); |
} |
|
/* Generate load and store state machine */ |
#if 0 |
GEN ("\n/* Load&store state machine */\n"); |
GEN ("always @(posedge clk or posedge rst)\n"); |
GEN (" if (rst) begin\n"); |
if (nloads) GEN (" l_stb <= #Tp %i'h0;\n", nloads); |
if (nstores) GEN (" s_stb <= #Tp %i'h0;\n", nstores); |
GEN (" end else begin\n"); |
for (i = 0; i < f->nmsched; i++) if (f->mtype[i] & MT_LOAD || f->mtype[i] & MT_STORE) { |
int cur = 0; |
dep_list *dep = f->INSN(f->msched[i]).dep; |
assert (f->INSN(f->msched[i]).opt[1] & (OPT_REF | OPT_REGISTER)); |
GEN (" if ("); |
print_deps (fo, f, REF_BB(f->msched[i]), f->INSN(f->msched[i]).dep, 1); |
GEN (") begin\n"); |
print_turn_off_dep (fo, f, dep); |
GEN (" %c_stb[%i] <= #Tp 1'b1;\n", f->mtype[i] & MT_LOAD ? 'l' : 's', cur++); |
GEN (" end\n"); |
} |
GEN (" if (%c_end[%i]) %c_stb <= #Tp %i'h0;\n", c, cur - 1, c, cur); |
GEN (" end\n"); |
#endif |
|
/* Generate state generator machine */ |
for (j = 0; j < 2; j++) { |
char c; |
char *s; |
|
switch (j) { |
case 0: c = 'l'; s = "Load"; break; |
case 1: c = 's'; s = "Store"; break; |
case 2: c = 'c'; s = "Calls"; break; |
} |
if (j == 0 && nloads |
|| j == 1 && nstores |
|| j == 2 && ncalls) { |
int cur = 0; |
char t[30]; |
|
GEN ("\n/* %s state generator machine */\n", s); |
GEN ("always @("); |
for (i = 0; i < f->nmsched; i++) { |
print_op_v (f, t, f->msched[i], 1); |
GEN ("%s or ", t); |
} |
GEN ("bb_start_r"); |
if (nloads) GEN (" or l_end"); |
if (nstores) GEN (" or s_end"); |
GEN (")\n"); |
GEN ("begin\n "); |
cucdebug (1, "%s\n", s); |
for (i = 0; i < f->nmsched; i++) |
if (j == 0 && f->mtype[i] & MT_LOAD |
|| j == 1 && f->mtype[i] & MT_STORE |
|| j == 2 && f->mtype[i] & MT_CALL) { |
dep_list *dep = f->INSN(f->msched[i]).dep; |
cucdebug (1, "msched[%i] = %x (mtype %x)\n", i, f->msched[i], f->mtype[i]); |
assert (f->INSN(f->msched[i]).opt[1] & (OPT_REF | OPT_REGISTER)); |
GEN ("if ("); |
print_deps (fo, f, REF_BB(f->msched[i]), f->INSN(f->msched[i]).dep, 1); |
GEN (") begin\n"); |
GEN (" %c_req_o = 1'b1;\n", c); |
GEN (" %c_sel_o[3:0] = 4'b", c); |
switch (f->mtype[i] & MT_WIDTH) { |
case 1: GEN ("0001 << (%s & 32'h3);\n", |
print_op_v (f, t, f->msched[i], 1)); break; |
case 2: GEN ("0011 << ((%s & 32'h1) << 1);\n", |
print_op_v (f, t, f->msched[i], 1)); break; |
case 4: GEN ("1111;\n"); break; |
default: assert (0); |
} |
GEN (" %c_linbrst_o = 1'b%i;\n", c, |
(f->mtype[i] & MT_BURST) && !(f->mtype[i] & MT_BURSTE) ? 1 : 0); |
GEN (" %c_adr_o = t%x_%x & ~32'h3;\n", c, |
REF_BB(f->INSN(f->msched[i]).op[1]), REF_I(f->INSN(f->msched[i]).op[1])); |
GEN (" end else "); |
} |
GEN ("if (%c_end[%i]) begin\n", c, cur - 1); |
GEN (" %c_req_o = 1'b0;\n", c); |
GEN (" %c_sel_o[3:0] = 4'bx;\n", c); |
GEN (" %c_linbrst_o = 1'b0;\n", c); |
GEN (" %c_adr_o = 32'hx;\n", c); |
GEN (" end else begin\n"); |
GEN (" %c_req_o = 1'b0;\n", c); |
GEN (" %c_sel_o[3:0] = 4'bx;\n", c); |
GEN (" %c_linbrst_o = 1'b0;\n", c); |
GEN (" %c_adr_o = 32'hx;\n", c); |
GEN (" end\n"); |
GEN ("end\n"); |
} |
} |
|
if (ncalls) { |
int cur_call = 0; |
GEN ("\n/* Function calls state machine */\n"); |
GEN ("always @(posedge clk or posedge rst)\n"); |
GEN ("begin\n"); |
GEN (" if (rst) begin\n"); |
GEN (" f_stb <= #Tp %i'h0;\n", nstores); |
for (i = 0; i < 6; i++) GEN (" fr%i_o <= #Tp 32'h0;\n", i + 3); |
if (log2_int(ncalls)) GEN (" fid_o <= #Tp %i'h0;\n", log2_int (f->nfdeps)); |
GEN (" fstart_o <= #Tp 1'b0;\n"); |
//GEN (" f11_r <= #Tp 32'h0;\n"); |
GEN (" end else begin\n"); |
cucdebug (1, "calls \n"); |
for (i = 0; i < f->nmsched; i++) if (f->mtype[i] & MT_CALL) { |
char t[30]; |
dep_list *dep = f->INSN(f->msched[i]).dep; |
cucdebug (1, "msched[%i] = %x (mtype %x)\n", i, f->msched[i], f->mtype[i]); |
assert (f->INSN(f->msched[i]).opt[1] & (OPT_REF | OPT_REGISTER)); |
GEN (" if ("); |
print_deps (fo, f, REF_BB(f->msched[i]), f->INSN(f->msched[i]).dep, 1); |
GEN (") begin\n"); |
print_turn_off_dep (fo, f, dep); |
GEN (" f_stb[%i] <= #Tp 1'b1;\n", cur_call++); |
GEN (" fstart_o <= #Tp 1'b1;\n"); |
if (log2_int (f->nfdeps)) |
GEN (" fid_o <= #Tp %i'h%x;\n", log2_int (f->nfdeps), func_index (f, f->msched[i])); |
|
for (j = 0; j < 6; j++) |
GEN (" fr%i_o <= #Tp t%x_%x;\n", j + 3, |
REF_BB (f->msched[i]), REF_I (f->msched[i]) - 6 + i); |
GEN (" end\n"); |
} |
GEN (" if (f_end[%i]) begin\n", ncalls - 1); |
GEN (" f_stb <= #Tp %i'h0;\n", ncalls); |
GEN (" f_start_o <= #Tp 1'b0;\n"); |
GEN (" end\n"); |
GEN (" end\n"); |
GEN ("end\n"); |
} |
|
GEN ("\n/* Basic blocks state machine */\n"); |
GEN ("always @(posedge clk or posedge rst)\n"); |
GEN ("begin\n"); |
GEN (" if (rst) bb_stb <= #Tp %i'h%x;\n", f->num_bb, 0); |
GEN (" else if (end_o) begin\n"); |
GEN (" bb_stb <= #Tp %i'h%x;\n", f->num_bb, 0); |
for (i = 0; i < f->num_bb; i++) { |
GEN (" end else if (bb_start[%i]) begin\n", i); |
GEN (" bb_stb <= #Tp %i'h%x;\n", f->num_bb, 1 << i); |
} |
GEN (" end else if (end_o) begin\n"); |
GEN (" bb_stb <= #Tp %i'h%x;\n", f->num_bb, 0); |
GEN (" end\n"); |
GEN ("end\n"); |
|
/* output footer */ |
GEN ("\nendmodule\n"); |
|
fclose (fo); |
} |
|
void generate_main (int nfuncs, cuc_func **f, char *filename) |
{ |
FILE *fo; |
int i, j, nrf, first; |
char tmp[256]; |
int ncallees[MAX_FUNCS]; |
int nl[MAX_FUNCS], ns[MAX_FUNCS]; |
int maxncallees = 0; |
sprintf (tmp, "%s_top.v", filename); |
|
for (i = 0, nrf = 0; i < nfuncs; i++) { |
nl[i] = ns[i] = 0; |
ncallees[i] = 0; |
if (f[i]) { |
f[i]->tmp = nrf++; |
for (j = 0; j < f[i]->nmsched; j++) |
if (f[i]->mtype[j] & MT_LOAD) nl[i]++; |
else if (f[i]->mtype[j] & MT_STORE) ns[i]++; |
for (j = 0; j < f[i]->nfdeps; j++) |
ncallees[f[i]->fdeps[j]->tmp]++; |
} |
} |
if (!nrf) return; |
|
for (i = 0; i < nrf; i++) |
if (maxncallees < ncallees[i]) maxncallees = ncallees[i]; |
|
log ("Generating verilog file \"%s\"\n", tmp); |
PRINTF ("Generating verilog file \"%s\"\n", tmp); |
if ((fo = fopen (tmp, "wt+")) == NULL) { |
fprintf (stderr, "Cannot open '%s'\n", tmp); |
exit (1); |
} |
|
/* output header */ |
GEN ("/* %s -- generated by Custom Unit Compiler\n", tmp); |
GEN (" (C) 2002 Opencores */\n\n"); |
GEN ("/* Includes %i functions:", nrf); |
for (i = 0; i < nfuncs; i++) if (f[i]) |
GEN ("\n%s", prof_func[i].name); |
GEN (" */\n\n"); |
|
GEN ("`include \"timescale.v\"\n\n"); |
GEN ("module %s (clk, rst,\n", filename); |
GEN (" /* Load and store master Wishbone ports */\n"); |
GEN (" l_adr_o, l_dat_i, l_cyc_o, l_stb_o,\n"); |
GEN (" l_sel_o, l_linbrst_o, l_rdy_i, l_we_o,\n"); |
GEN (" s_adr_o, s_dat_o, s_cyc_o, s_stb_o,\n"); |
GEN (" s_sel_o, s_linbrst_o, s_rdy_i, s_we_o,\n\n"); |
GEN (" /* cuc interface */\n"); |
GEN (" cuc_stb_i, cuc_adr_i, cuc_dat_i, cuc_dat_o, cuc_we_i, cuc_rdy_o);\n\n"); |
|
GEN ("parameter Tp = 1;\n"); |
GEN ("\n/* module ports */\n"); |
GEN ("input clk, rst, cuc_stb_i, cuc_we_i;\n"); |
GEN ("input l_rdy_i, s_rdy_i;\n"); |
GEN ("output l_cyc_o, l_stb_o, l_we_o, l_linbrst_o;\n"); |
GEN ("reg l_cyc_o, l_stb_o, l_we_o, l_linbrst_o;\n"); |
GEN ("output s_cyc_o, s_stb_o, s_we_o, s_linbrst_o;\n"); |
GEN ("reg s_cyc_o, s_stb_o, s_we_o, s_linbrst_o;\n"); |
GEN ("output cuc_rdy_o; /* Not registered ! */\n"); |
GEN ("output [3:0] l_sel_o, s_sel_o;\n"); |
GEN ("reg [3:0] l_sel_o, s_sel_o;\n"); |
GEN ("output [31:0] l_adr_o, s_adr_o, s_dat_o, cuc_dat_o;\n"); |
GEN ("reg [31:0] l_adr_o, s_adr_o, s_dat_o, cuc_dat_o;\n"); |
GEN ("input [15:0] cuc_adr_i;\n"); |
GEN ("input [31:0] l_dat_i, cuc_dat_i;\n\n"); |
|
GEN ("wire [%2i:0] i_we, i_re, i_finish, i_selected, i_first_reg;\n", nrf - 1); |
GEN ("wire [%2i:0] i_bidok, i_start_bid, i_start_bidok, main_start, main_end;\n", nrf - 1); |
GEN ("wire [%2i:0] i_start, i_end, i_start_block, i_busy;\n", nrf - 1); |
GEN ("wire [%2i:0] i_l_req, i_s_req;\n", nrf - 1); |
GEN ("reg [%2i:0] i_go_bsy, main_start_r;\n", nrf - 1); |
|
GEN ("assign i_selected = {\n"); |
for (i = 0; i < nrf; i++) |
GEN (" cuc_adr_i[15:6] == %i%s\n", i, i < nrf - 1 ? "," : "};"); |
|
GEN ("assign i_first_reg = {\n"); |
for (i = 0; i < nfuncs; i++) if (f[i]) { |
for (j = 0; j <= MAX_REGS; j++) if (f[i]->used_regs[j]) break; |
GEN (" cuc_adr_i[5:0] == %i%s\n", j, f[i]->tmp < nrf - 1 ? "," : "};"); |
} |
|
GEN ("assign i_we = {%i{cuc_stb_i && cuc_we_i}} & i_selected;\n", nrf); |
GEN ("assign i_re = {%i{cuc_stb_i && !cuc_we_i}} & i_selected;\n", nrf); |
|
GEN ("assign i_start = i_go_bsy & {%i{cuc_rdy_o}};\n", nrf); |
GEN ("assign i_start_bidok = {\n"); |
for (i = 0; i < nrf; i++) |
GEN (" i_start_bid[%i] < %i%s\n", i, i, i < nrf - 1 ? "," : "};"); |
GEN ("assign main_start = i_start & i_selected & i_first_reg & i_we;\n"); |
GEN ("assign main_end = {%i{i_end}} & i_selected;\n"); |
|
GEN ("\nalways @(posedge clk or posedge rst)\n"); |
GEN ("begin\n"); |
GEN (" if (rst) i_go_bsy <= #Tp %i'b0;\n", nrf); |
GEN (" else i_go_bsy <= #Tp i_we | ~i_finish & i_go_bsy;\n"); |
GEN ("end\n"); |
|
|
/* Function specific data */ |
for (i = 0; i < nfuncs; i++) if (f[i]) { |
int ci = 0, co = 0; |
int fn = f[i]->tmp; |
GEN ("\n/* Registers for function %s */\n", prof_func[i].name); |
for (j = 0, first = 1; j < MAX_REGS; j++) if (f[i]->used_regs[j]) { |
GEN ("%s i%i_r%ii", first ? "/* inputs */\nreg [31:0]" : ",", fn, j); |
first = 0; |
ci++; |
} |
if (ci) GEN (";\n"); |
|
for (j = 0, first = 1; j < MAX_REGS; j++) |
if (f[i]->lur[j] >= 0 && !f[i]->saved_regs[j]) { |
GEN ("%s i%i_r%io", first ? "/* outputs */\nwire [31:0]" : ",", fn, j); |
first = 0; |
co++; |
} |
if (co) GEN (";\n"); |
GEN ("wire [31:0] i%i_l_adr, i%i_s_adr;\n", fn, fn); |
|
GEN ("always @(posedge clk or posedge rst)\n"); |
GEN (" if (rst) main_start_r <= #Tp %i'b0;\n", nrf); |
GEN (" else main_start_r <= #Tp main_start & i_start_bidok | i_busy | ~i_end & main_start_r;\n"); |
|
if (ci) { |
GEN ("\n/* write register access */\n"); |
GEN ("always @(posedge clk or posedge rst)\n"); |
GEN ("begin\n"); |
GEN (" if (rst) begin\n"); |
for (j = 0; j < MAX_REGS; j++) if (f[i]->used_regs[j]) |
GEN (" i%i_r%ii <= #Tp 32'h0;\n", fn, j); |
GEN (" end else if (!i_go_bsy[%i] && i_we[%i])\n", fn, fn); |
GEN (" case (cuc_adr_i[5:0])\n"); |
for (j = 0; j < MAX_REGS; j++) if (f[i]->used_regs[j]) |
GEN (" %-2i: i%i_r%ii <= #Tp cuc_dat_i;\n", j, fn, j); |
GEN (" endcase\n"); |
GEN ("end\n"); |
} |
|
GEN ("\n"); |
} |
|
/* Generate machine for reading all function registers. Register read can be |
delayed till function completion */ |
{ |
int co; |
GEN ("/* read register access - data */\n"); |
GEN ("always @(posedge clk or posedge rst)\n"); |
GEN (" if (rst) cuc_dat_o <= #Tp 32'h0;\n"); |
GEN (" else if (cuc_stb_i && cuc_we_i) begin\n"); |
GEN (" "); |
|
for (i = 0; i < nfuncs; i++) if (f[i]) { |
co = 0; |
for (j = 0; j < MAX_REGS; j++) |
if (f[i]->lur[j] >= 0 && !f[i]->saved_regs[j]) co++; |
|
GEN ("if (cuc_adr_i[15:6] == %i)", f[i]->tmp); |
if (co) { |
first = 1; |
GEN ("\n case (cuc_adr_i[5:0])\n"); |
for (j = 0; j < MAX_REGS; j++) |
if (f[i]->lur[j] >= 0 && !f[i]->saved_regs[j]) |
GEN (" %-2i: cuc_dat_o <= #Tp i%i_r%io;\n", j, f[i]->tmp, j); |
GEN (" endcase\n"); |
} else { |
GEN (" cuc_dat_o <= #Tp 32'hx;\n"); |
} |
GEN (" else "); |
} |
GEN ("cuc_dat_o <= #Tp 32'hx;\n"); |
GEN (" end else cuc_dat_o <= #Tp 32'hx;\n"); |
|
GEN ("\n/* read register access - acknowledge */\n"); |
GEN ("assign cuc_rdy_o = cuc_stb_i && cuc_we_i && |(i_selected & main_end);\n"); |
} |
|
/* Store/load Wishbone bridge */ |
for (j = 0; j < 2; j++) { |
char t = j ? 's' : 'l'; |
GEN ("\n/* %s Wishbone bridge */\n", j ? "store" : "load"); |
GEN ("reg [%i:0] %cm_sel;\n", log2_int (nrf), t); |
GEN ("reg [%i:0] %cm_bid;\n", log2_int (nrf), t); |
GEN ("reg %ccyc_ip;\n\n", t); |
GEN ("always @(posedge clk)\n"); |
GEN ("begin\n"); |
GEN (" %c_we_o <= #Tp 1'b%i;\n", t, j); |
GEN (" %c_cyc_o <= #Tp |i_%c_req;\n", t, t); |
GEN (" %c_stb_o <= #Tp |i_%c_req;\n", t, t); |
GEN ("end\n"); |
|
GEN ("\n/* highest bid */\n"); |
GEN ("always @("); |
for (i = 0; i < nrf; i++) GEN ("%si_%c_req", i > 0 ? " or " : "", t); |
GEN (")\n"); |
for (i = 0; i < nrf; i++) GEN (" %sif (i_%c_req) %cm_bid = %i'h%x;\n", |
i ? "else " : "", t, t, log2_int (nrf) + 1, i); |
|
GEN ("\n/* selected transfer */\n"); |
GEN ("always @(posedge clk or posedge rst)\n"); |
GEN (" if (rst) %cm_sel <= #Tp %i'h0;\n", t, log2_int (nrf) + 1); |
GEN (" else if (%c_rdy_i) %cm_sel <= #Tp %i'h0;\n", t, t, log2_int (nrf) + 1); |
GEN (" else if (!%ccyc_ip) %cm_sel <= #Tp %cm_bid;\n", t, t, t); |
|
GEN ("\n/* Cycle */\n"); |
GEN ("\nalways @(posedge clk or posedge rst)\n"); |
GEN (" if (rst) %ccyc_ip <= #Tp 1'b0;\n", t); |
GEN (" else if (%c_rdy_i) %ccyc_ip <= #Tp 1'b0;\n", t, t); |
GEN (" else %ccyc_ip <= #Tp %c_cyc_o;\n", t, t); |
} |
|
GEN ("\n/* Acknowledge */\n"); |
for (i = 0; i < nrf; i++) { |
GEN ("wire i%i_s_rdy = ((sm_bid == %i & !scyc_ip) | sm_sel == %i) & s_rdy_i;\n", i, i, i); |
GEN ("wire i%i_l_rdy = ((lm_bid == %i & !lcyc_ip) | lm_sel == %i) & l_rdy_i;\n", i, i, i); |
} |
|
GEN ("\n/* data, address selects and burst enables */\n"); |
for (i = 0; i < nrf; i++) GEN ("wire [31:0] i%i_s_dat;\n", i); |
for (i = 0; i < nrf; i++) GEN ("wire i%i_s_linbrst, i%i_l_linbrst;\n", i, i); |
for (i = 0; i < nrf; i++) GEN ("wire [3:0] i%i_s_sel, i%i_l_sel;\n", i, i); |
for (i = 0; i < nrf; i++) GEN ("wire [31:0] i%i_l_dat = l_dat_i;\n", i); |
GEN ("\nalways @(posedge clk)\n"); |
GEN ("begin\n"); |
GEN (" s_dat_o <= #Tp "); |
for (i = 0; i < nrf - 1; i++) |
GEN ("\n sm_bid == %i ? i%i_s_dat : ", i, i); |
GEN ("i%i_s_dat;\n", nrf - 1); |
GEN (" s_adr_o <= #Tp "); |
for (i = 0; i < nrf - 1; i++) |
GEN ("\n sm_bid == %i ? i%i_s_adr : ", i, i); |
GEN ("i%i_s_adr;\n", nrf - 1); |
GEN (" s_sel_o <= #Tp "); |
for (i = 0; i < nrf - 1; i++) |
GEN ("\n sm_bid == %i ? i%i_s_sel : ", i, i); |
GEN ("i%i_s_sel;\n", nrf - 1); |
GEN (" s_linbrst_o <= #Tp "); |
for (i = 0; i < nrf - 1; i++) |
GEN ("\n sm_bid == %i ? i%i_s_linbrst : ", i, i); |
GEN ("i%i_s_linbrst;\n", nrf - 1); |
GEN ("end\n\n"); |
|
GEN ("always @(posedge clk)\n"); |
GEN ("begin\n"); |
GEN (" l_adr_o <= #Tp "); |
for (i = 0; i < nrf - 1; i++) |
GEN ("\n lm_bid == %i ? i%i_l_adr : ", i, i); |
GEN ("i%i_l_adr;\n", nrf - 1); |
GEN (" l_sel_o <= #Tp "); |
for (i = 0; i < nrf - 1; i++) |
GEN ("\n lm_bid == %i ? i%i_l_sel : ", i, i); |
GEN ("i%i_l_sel;\n", nrf - 1); |
GEN (" l_linbrst_o <= #Tp "); |
for (i = 0; i < nrf - 1; i++) |
GEN ("\n lm_bid == %i ? i%i_l_linbrst : ", i, i); |
GEN ("i%i_l_linbrst;\n", nrf - 1); |
GEN ("end\n\n"); |
|
/* start/end signals */ |
GEN ("\n\n/* start/end signals */\n"); |
for (i = 0; i < nrf; i++) { |
if (log2_int (maxncallees + 1)) |
GEN ("wire [%i:0] i%i_current = i%i_busy ? i%i_current_r : i%i_start_bid;\n", |
log2_int (maxncallees + 1), i, i, i, i, i); |
else GEN ("wire i%i_current = 0;\n", i); |
} |
GEN ("\n"); |
|
for (i = 0, j = 0; i < nfuncs; i++) if (f[i]) { |
if (log2_int (ncallees[i])) { |
GEN ("reg [%i:0] i%i_start_bid;\n", log2_int (ncallees[i]), j); |
GEN ("always @(start%i", f[i]->tmp); |
for (j = 0, first = 1; j < f[i]->nfdeps; j++) |
if (f[i]->fdeps[j]) GEN (", "); |
GEN (")\n"); |
GEN ("begin !!!\n"); //TODO |
GEN (" \n"); |
GEN ("end\n"); |
} |
GEN ("wire i%i_start = main_start[%i];\n", j, j); |
j++; |
} |
GEN ("\n"); |
|
for (i = 0; i < nfuncs; i++) if (f[i]) { |
int nf = f[i]->tmp; |
GEN ("\n%s%s i%i(.clk(clk), .rst(rst),\n", filename, prof_func[i].name, nf); |
GEN (""); |
GEN (" .l_adr_o(i%i_l_adr), .l_dat_i(i%i_l_dat), .l_req_o(i_l_req[%i]),\n", |
nf, nf, nf); |
GEN (" .l_sel_o(i%i_l_sel), .l_linbrst_o(i%i_l_linbrst), .l_rdy_i(i%i_l_rdy),\n", |
nf, nf, nf); |
GEN (" .s_adr_o(i%i_s_adr), .s_dat_o(i%i_s_dat), .s_req_o(i_s_req[%i]),\n", |
nf, nf, nf); |
GEN (" .s_sel_o(i%i_s_sel), .s_linbrst_o(i%i_s_linbrst), .s_rdy_i(i%i_s_rdy),\n", |
nf, nf, nf); |
GEN (" "); |
for (j = 0, first = 1; j < MAX_REGS; j++) if (f[i]->used_regs[j]) |
GEN (".r%i_i(i%i_r%ii), ", j, nf, j), first = 0; |
|
if (first) GEN ("\n "); |
for (j = 0, first = 1; j < MAX_REGS; j++) |
if (f[i]->lur[j] >= 0 && !f[i]->saved_regs[j]) |
GEN (".r%i_o(i%i_r%io), ", j, nf, j), first = 0; |
if (first) GEN ("\n "); |
if (f[i]->nfdeps) { |
GEN (".fstart_o(i_fstart[%i]), .fend_i(i_fend[%i]), .fid_o(i%i_fid),\n", i, i, i), |
GEN (" .fr3_o(i%i_fr3), .fr4_o(i%i_fr4), .fr5_o(i%i_fr5), .fr6_o(i%i_fr6),\n"); |
GEN (" .fr7_o(i%i_fr7), .fr8_o(i%i_fr8), .fr11_i(i%i_fr11i),\n "); |
} |
GEN (".start_i(i_start[%i]), .end_o(i_end[%i]), .busy_o(i_busy[%i]));\n", nf, nf, nf); |
} |
|
/* output footer */ |
GEN ("\nendmodule\n"); |
|
fclose (fo); |
} |
|
/trunk/or1ksim/cuc/adv.c
41,7 → 41,7
|
static unsigned long mask (unsigned long c) |
{ |
if (c) return (1 << (log2 (c) + 1)) - 1; |
if (c) return (1 << (log2_int (c) + 1)) - 1; |
else return 0; |
} |
|
/trunk/or1ksim/cuc/cuc.c
1,824 → 1,825
/* cuc.c -- OpenRISC Custom Unit Compiler |
* Copyright (C) 2002 Marko Mlinar, markom@opencores.org |
* |
* This file is part of OpenRISC 1000 Architectural Simulator. |
* |
* This program is free software; you can redistribute it and/or modify |
* it under the terms of the GNU General Public License as published by |
* the Free Software Foundation; either version 2 of the License, or |
* (at your option) any later version. |
* |
* This program is distributed in the hope that it will be useful, |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
* GNU General Public License for more details. |
* |
* You should have received a copy of the GNU General Public License |
* along with this program; if not, write to the Free Software |
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ |
|
/* Main file, including code optimization and command prompt */ |
|
#include <stdio.h> |
#include <stdlib.h> |
#include <stdarg.h> |
#include <assert.h> |
#include <ctype.h> |
#include "sim-config.h" |
#include "cuc.h" |
#include "insn.h" |
#include "profiler.h" |
#include "opcode/or32.h" |
#include "parse.h" |
|
FILE *flog; |
int cuc_debug = 0; |
|
/* Last used registers by software convention */ |
/* Note that r11 is caller saved register, and we can destroy it. |
Due to CUC architecture we must always return something, even garbage (so that |
caller knows, we are finished, when we send acknowledge). |
In case r11 was not used (trivial register assignment) we will remove it later, |
but if we assigned a value to it, it must not be removed, so caller_saved[11] = 0 */ |
const int caller_saved[MAX_REGS] = { |
0, 0, 0, 1, 1, 1, 1, 1, |
1, 1, 0, 0, 0, 1, 0, 1, |
0, 1, 0, 1, 0, 1, 0, 1, |
0, 1, 0, 1, 0, 1, 0, 1, |
1, 1}; |
|
/* returns log2(x) */ |
int log2 (unsigned long x) |
{ |
int c = 0; |
assert (x >= 0); |
if (!x) return 0; /* not by the book, but practical */ |
while (x != 1) x >>= 1, c++; |
return c; |
} |
|
/* Does all known instruction optimizations */ |
void cuc_optimize (cuc_func *func) |
{ |
int modified = 0; |
int first = 1; |
log ("Optimizing.\n"); |
do { |
modified = 0; |
clean_deps (func); |
if (cuc_debug >= 6) print_cuc_bb (func, "AFTER_CLEAN_DEPS"); |
if (optimize_cmovs (func)) { |
if (cuc_debug >= 6) print_cuc_bb (func, "AFTER_OPT_CMOVS"); |
modified = 1; |
} |
if (cuc_debug) cuc_check (func); |
if (optimize_tree (func)) { |
if (cuc_debug >= 6) print_cuc_bb (func, "AFTER_OPT_TREE1"); |
modified = 1; |
} |
if (remove_nops (func)) { |
if (cuc_debug >= 6) print_cuc_bb (func, "NO_NOPS"); |
modified = 1; |
} |
if (cuc_debug) cuc_check (func); |
if (remove_dead (func)) { |
if (cuc_debug >= 5) print_cuc_bb (func, "AFTER_DEAD"); |
modified = 1; |
} |
if (cuc_debug) cuc_check (func); |
if (cse (func)) { |
log ("Common subexpression elimination.\n"); |
if (cuc_debug >= 3) print_cuc_bb (func, "AFTER_CSE"); |
modified = 1; |
} |
if (first) { |
insert_conditional_facts (func); |
if (cuc_debug >= 3) print_cuc_bb (func, "AFTER_COND_FACT"); |
if (cuc_debug) cuc_check (func); |
first = 0; |
} |
if (optimize_bb (func)) { |
if (cuc_debug >= 5) print_cuc_bb (func, "AFTER_OPT_BB"); |
modified = 1; |
} |
if (cuc_debug) cuc_check (func); |
if (remove_nops (func)) { |
if (cuc_debug >= 6) print_cuc_bb (func, "NO_NOPS"); |
modified = 1; |
} |
if (remove_dead_bb (func)) { |
if (cuc_debug >= 5) print_cuc_bb (func, "AFTER_DEAD_BB"); |
modified = 1; |
} |
if (remove_trivial_regs (func)) { |
if (cuc_debug >= 2) print_cuc_bb (func, "AFTER_TRIVIAL"); |
modified = 1; |
} |
if (remove_nops (func)) { |
if (cuc_debug >= 6) print_cuc_bb (func, "NO_NOPS"); |
modified = 1; |
} |
add_memory_dep (func, func->memory_order); |
if (cuc_debug >= 7) print_cuc_bb (func, "AFTER_MEMORY_DEP"); |
add_data_dep (func); |
if (cuc_debug >= 8) print_cuc_bb (func, "AFTER_DATA_DEP"); |
if (schedule_memory (func, func->memory_order)) { |
if (cuc_debug >= 7) print_cuc_bb (func, "AFTER_SCHEDULE_MEM"); |
modified = 1; |
} |
} while (modified); |
set_io (func); |
#if 0 |
detect_max_values (func); |
if (cuc_debug >= 5) print_cuc_bb (func, "AFTER_MAX_VALUES"); |
#endif |
} |
|
/* Pre/unrolls basic block and optimizes it */ |
cuc_timings *preunroll_bb (char *bb_filename, cuc_func *f, cuc_timings *timings, int b, int i, int j) |
{ |
cuc_func *func; |
cucdebug (2, "BB%i unroll %i times preroll %i times\n", b, j, i); |
log ("BB%i unroll %i times preroll %i times\n", b, j, i); |
func = preunroll_loop (f, b, i, j, bb_filename); |
if (cuc_debug >= 2) print_cuc_bb (func, "AFTER_PREUNROLL"); |
cuc_optimize (func); |
analyse_timings (func, timings); |
|
cucdebug (2, "new_time = %i, old_time = %i, size = %f\n", |
timings->new_time, func->orig_time, timings->size); |
log ("new time = %icyc, old_time = %icyc, size = %.0f gates\n", |
timings->new_time, func->orig_time, timings->size); |
//output_verilog (func, argv[1]); |
free_func (func); |
timings->b = b; |
timings->unroll = j; |
timings->preroll = i; |
timings->nshared = 0; |
return timings; |
} |
|
/* Simple comparison function */ |
int tim_comp (cuc_timings *a, cuc_timings *b) |
{ |
if (a->new_time < b->new_time) return -1; |
else if (a->new_time > b->new_time) return 1; |
else return 0; |
} |
|
/* Analyses function; done when cuc command is entered in (sim) prompt */ |
cuc_func *analyse_function (char *module_name, long orig_time, |
unsigned long start_addr, unsigned long end_addr, |
int memory_order, int num_runs) |
{ |
cuc_timings timings; |
cuc_func *func = (cuc_func *) malloc (sizeof (cuc_func)); |
cuc_func *saved; |
int b, i, j; |
char tmp1[256]; |
char tmp2[256]; |
|
func->orig_time = orig_time; |
func->start_addr = start_addr; |
func->end_addr = end_addr; |
func->memory_order = memory_order; |
func->nfdeps = 0; |
func->fdeps = NULL; |
func->num_runs = num_runs; |
|
sprintf (tmp1, "%s.bin", module_name); |
cucdebug (2, "Loading %s.bin\n", module_name); |
if (cuc_load (tmp1)) { |
free (func); |
return NULL; |
} |
|
log ("Detecting basic blocks\n"); |
detect_bb (func); |
if (cuc_debug >= 2) print_cuc_insns ("WITH_BB_LIMITS", 0); |
|
//sprintf (tmp1, "%s.bin.mp", module_name); |
sprintf (tmp2, "%s.bin.bb", module_name); |
generate_bb_seq (func, config.sim.mprof_fn, tmp2); |
log ("Assuming %i clk cycle load (%i cyc burst)\n", runtime.cuc.mdelay[0], runtime.cuc.mdelay[2]); |
log ("Assuming %i clk cycle store (%i cyc burst)\n", runtime.cuc.mdelay[1], runtime.cuc.mdelay[3]); |
|
build_bb (func); |
if (cuc_debug >= 5) print_cuc_bb (func, "AFTER_BUILD_BB"); |
reg_dep (func); |
|
log ("Detecting dependencies\n"); |
if (cuc_debug >= 2) print_cuc_bb (func, "AFTER_REG_DEP"); |
cuc_optimize (func); |
|
#if 0 |
csm (func); |
#endif |
assert (saved = dup_func (func)); |
|
timings.preroll = timings.unroll = 1; |
timings.nshared = 0; |
|
add_latches (func); |
if (cuc_debug >= 1) print_cuc_bb (func, "AFTER_LATCHES"); |
analyse_timings (func, &timings); |
|
free_func (func); |
log ("Base option: pre%i,un%i,sha%i: %icyc %.1f\n", |
timings.preroll, timings.unroll, timings.nshared, timings.new_time, timings.size); |
saved->timings = timings; |
|
#if 1 |
/* detect and unroll simple loops */ |
for (b = 0; b < saved->num_bb; b++) { |
cuc_timings t[MAX_UNROLL * MAX_PREROLL]; |
cuc_timings *ut; |
cuc_timings *cut = &t[0]; |
int nt = 1; |
double csize; |
saved->bb[b].selected_tim = -1; |
|
/* Is it a loop? */ |
if (saved->bb[b].next[0] != b && saved->bb[b].next[1] != b) continue; |
log ("Found loop at BB%x. Trying to unroll.\n", b); |
t[0] = timings; |
t[0].b = b; |
t[0].preroll = 1; |
t[0].unroll = 1; |
t[0].nshared = 0; |
|
sprintf (tmp1, "%s.bin.bb", module_name); |
i = 1; |
do { |
cuc_timings *pt; |
cuc_timings *cpt = cut; |
j = 1; |
|
do { |
pt = cpt; |
cpt = preunroll_bb (tmp1, saved, &t[nt++], b, ++j, i); |
} while (j <= MAX_PREROLL && pt->new_time > cpt->new_time); |
i++; |
ut = cut; |
cut = preunroll_bb (tmp1, saved, &t[nt++], b, 1, i); |
} while (i <= MAX_UNROLL && ut->new_time > cut->new_time); |
|
/* Sort the timings */ |
#if 0 |
if (cuc_debug >= 3) |
for (i = 0; i < nt; i++) PRINTF ("%i:%i,%i: %icyc\n", |
t[i].b, t[i].preroll, t[i].unroll, t[i].new_time); |
#endif |
|
qsort (t, nt, sizeof (cuc_timings), (int (*)(const void *, const void *))tim_comp); |
|
/* Delete timings, that have worst time and bigger size than other */ |
j = 1; |
csize = t[0].size; |
for (i = 1; i < nt; i++) |
if (t[i].size < csize) t[j++] = t[i]; |
nt = j; |
|
cucdebug (1, "Available options\n"); |
for (i = 0; i < nt; i++) cucdebug (1, "%i:%i,%i: %icyc %.1f\n", |
t[i].b, t[i].preroll, t[i].unroll, t[i].new_time, t[i].size); |
/* Add results from CSM */ |
j = nt; |
for (i = 0; i < saved->bb[b].ntim; i++) { |
int i1; |
for (i1 = 0; i1 < nt; i1++) { |
t[j] = t[i1]; |
t[j].size += saved->bb[b].tim[i].size - timings.size; |
t[j].new_time += saved->bb[b].tim[i].new_time - timings.new_time; |
t[j].nshared = saved->bb[b].tim[i].nshared; |
t[j].shared = saved->bb[b].tim[i].shared; |
if (++j >= MAX_UNROLL * MAX_PREROLL) goto full; |
} |
} |
|
full: |
nt = j; |
|
cucdebug (1, "Available options:\n"); |
for (i = 0; i < nt; i++) cucdebug (1, "%i:%i,%i: %icyc %.1f\n", |
t[i].b, t[i].preroll, t[i].unroll, t[i].new_time, t[i].size); |
|
/* Sort again with new timings added */ |
qsort (t, nt, sizeof (cuc_timings), (int (*)(const void *, const void *))tim_comp); |
|
/* Delete timings, that have worst time and bigger size than other */ |
j = 1; |
csize = t[0].size; |
for (i = 1; i < nt; i++) |
if (t[i].size < csize) t[j++] = t[i]; |
nt = j; |
|
cucdebug (1, "Available options:\n"); |
for (i = 0; i < nt; i++) cucdebug (1, "%i:%i,%i: %icyc %.1f\n", |
t[i].b, t[i].preroll, t[i].unroll, t[i].new_time, t[i].size); |
|
if (saved->bb[b].ntim) free (saved->bb[b].tim); |
saved->bb[b].ntim = nt; |
assert (saved->bb[b].tim = (cuc_timings *) malloc (sizeof (cuc_timings) * nt)); |
|
/* Copy options in reverse order -- smallest first */ |
for (i = 0; i < nt; i++) saved->bb[b].tim[i] = t[nt - 1 - i]; |
|
log ("Available options:\n"); |
for (i = 0; i < saved->bb[b].ntim; i++) { |
log ("%i:pre%i,un%i,sha%i: %icyc %.1f\n", |
saved->bb[b].tim[i].b, saved->bb[b].tim[i].preroll, saved->bb[b].tim[i].unroll, |
saved->bb[b].tim[i].nshared, saved->bb[b].tim[i].new_time, saved->bb[b].tim[i].size); |
} |
} |
#endif |
return saved; |
} |
|
/* Utility option formatting functions */ |
static const char *option_char = "?abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; |
|
/*static */char *gen_option (char *s, int bb_no, int f_opt) |
{ |
if (bb_no >= 0) sprintf (s, "%i", bb_no); |
assert (f_opt <= strlen (option_char)); |
sprintf (s, "%s%c", s, option_char[f_opt]); |
return s; |
} |
|
/*static */void print_option (int bb_no, int f_opt) |
{ |
char tmp1[10]; |
char tmp2[10]; |
sprintf (tmp2, "%s", gen_option (tmp1, bb_no, f_opt)); |
PRINTF ("%3s", tmp2); |
} |
|
static char *format_func_options (char *s, cuc_func *f) |
{ |
int b, first = 1; |
*s = '\0'; |
for (b = 0; b < f->num_bb; b++) |
if (f->bb[b].selected_tim >= 0) { |
char tmp[10]; |
sprintf (s, "%s%s%s", s, first ? "" : ",", gen_option (tmp, b, f->bb[b].selected_tim)); |
first = 0; |
} |
return s; |
} |
|
static void options_cmd (int func_no, cuc_func *f) |
{ |
int b, i; |
char tmp[30]; |
char *name = prof_func[func_no].name; |
PRINTF ("-----------------------------------------------------------------------------\n"); |
PRINTF ("|%-28s|pre/unrolled|shared| time | gates |old_time|\n", |
strstrip (tmp, name, 28)); |
PRINTF ("| BASE |%4i / %4i | %4i |%8i|%8.f|%8i|\n", 1, 1, 0, |
f->timings.new_time, f->timings.size, f->orig_time); |
for (b = 0; b < f->num_bb; b++) { |
/* Print out results */ |
for (i = 1; i < f->bb[b].ntim; i++) { /* First one is base option */ |
int time = f->bb[b].tim[i].new_time - f->timings.new_time; |
double size = f->bb[b].tim[i].size - f->timings.size; |
PRINTF ("| "); |
print_option (b, i); |
PRINTF (" |%4i / %4i | %4i |%+8i|%+8.f| |\n", |
f->bb[b].tim[i].preroll, f->bb[b].tim[i].unroll, f->bb[b].tim[i].nshared, |
time, size); |
} |
} |
} |
|
/* Generates a function, based on specified parameters */ |
cuc_func *generate_function (cuc_func *rf, char *name, char *cut_filename) |
{ |
int b, i, j; |
char tmp[256]; |
cuc_timings tt; |
cuc_func *f; |
assert (f = dup_func (rf)); |
|
if (cuc_debug >= 2) print_cuc_bb (f, "BEFORE_GENERATE"); |
log ("Generating function %s.\n", name); |
PRINTF ("Generating function %s.\n", name); |
|
format_func_options (tmp, rf); |
if (strlen (tmp)) PRINTF ("Applying options: %s\n", tmp); |
else PRINTF ("Using basic options.\n"); |
|
/* Generate function as specified by options */ |
for (b = 0; b < f->num_bb; b++) { |
cuc_timings *st; |
if (rf->bb[b].selected_tim < 0) continue; |
st = &rf->bb[b].tim[rf->bb[b].selected_tim]; |
sprintf (tmp, "%s.bin.bb", name); |
preunroll_bb (&tmp[0], f, &tt, b, st->preroll, st->unroll); |
if (cuc_debug >= 1) print_cuc_bb (f, "AFTER_PREUNROLL"); |
} |
for (b = 0; b < f->num_bb; b++) { |
cuc_timings *st; |
if (rf->bb[b].selected_tim < 0) continue; |
st = &rf->bb[b].tim[rf->bb[b].selected_tim]; |
if (!st->nshared) continue; |
assert (0); |
//csm_gen (f, rf, st->nshared, st->shared); |
} |
add_latches (f); |
if (cuc_debug >= 1) print_cuc_bb (f, "AFTER_LATCHES"); |
analyse_timings (f, &tt); |
|
sprintf (tmp, "%s%s", cut_filename, name); |
output_verilog (f, tmp, name); |
return f; |
} |
|
/* Calculates required time, based on selected options */ |
int calc_cycles (cuc_func *f) |
{ |
int b, i, ntime = f->timings.new_time; |
for (b = 0; b < f->num_bb; b++) |
if (f->bb[b].selected_tim >= 0) { |
assert (f->bb[b].selected_tim < f->bb[b].ntim); |
ntime += f->bb[b].tim[f->bb[b].selected_tim].new_time - f->timings.new_time; |
} |
return ntime; |
} |
|
/* Calculates required size, based on selected options */ |
double calc_size (cuc_func *f) |
{ |
int b, i; |
double size = f->timings.size; |
for (b = 0; b < f->num_bb; b++) |
if (f->bb[b].selected_tim >= 0) { |
assert (f->bb[b].selected_tim < f->bb[b].ntim); |
size += f->bb[b].tim[f->bb[b].selected_tim].size - f->timings.size; |
} |
return size; |
} |
|
/* Dumps specified function to file (hex) */ |
unsigned long extract_function (char *out_fn, unsigned long start_addr) |
{ |
FILE *fo; |
unsigned long a = start_addr; |
int x = 0; |
assert (fo = fopen (out_fn, "wt+")); |
|
do { |
unsigned long d = evalsim_mem32 (a); |
int index = insn_decode (d); |
assert (index >= 0); |
if (x) x++; |
if (strcmp (insn_name (index), "l.jr") == 0) x = 1; |
a += 4; |
fprintf (fo, "%08x\n", d); |
} while (x < 2); |
|
fclose (fo); |
return a - 4; |
} |
|
static cuc_func *func[MAX_FUNCS]; |
static int func_v[MAX_FUNCS]; |
|
/* Detects function dependencies and removes */ |
static void set_func_deps () |
{ |
int f, b, i, j; |
restart: |
for (f = 0; f < prof_nfuncs - 1; f++) if (func[f]) { |
int fused[MAX_FUNCS] = {0}; |
int c; |
for (b = 0; b < func[f]->num_bb; b++) |
for (i = 0; i < func[f]->bb[b].ninsn; i++) { |
cuc_insn *ii = &func[f]->bb[b].insn[i]; |
if (ii->index == II_CALL) { |
assert (ii->opt[0] == OPT_CONST); |
for (j = 0; j < prof_nfuncs - 1; j++) |
if (func[j] && func[j]->start_addr == ii->op[0]) break; |
if (j >= prof_nfuncs - 1) { |
log ("%s is calling unknown function, address %08x\n", |
prof_func[f].name, ii->op[0]); |
debug (1, "%s is calling unknown function, address %08x\n", |
prof_func[f].name, ii->op[0]); |
free_func (func[f]); |
func[f] = NULL; |
goto restart; |
} else if (f == j) { |
log ("%s is recursive, ignoring\n", prof_func[f].name); |
debug (1, "%s is recursive, ignoring\n", prof_func[f].name); |
free_func (func[f]); |
func[f] = NULL; |
goto restart; |
} else fused[j]++; |
} |
} |
for (i = 0; i < MAX_FUNCS; i++) if (fused[i]) c++; |
if (func[f]->nfdeps) free (func[f]->fdeps); |
func[f]->nfdeps = c; |
func[f]->fdeps = (cuc_func **) malloc (sizeof (cuc_func *) * c); |
for (i = 0, j = 0; i < MAX_FUNCS; i++) |
if (fused[i]) func[f]->fdeps[j++] = func[i]; |
} |
|
/* Detect loops */ |
{ |
int change; |
for (f = 0; f < MAX_FUNCS; f++) if (func[f]) func[f]->tmp = 0; |
do { |
change = 0; |
for (f = 0; f < MAX_FUNCS; f++) if (func[f] && !func[f]->tmp) { |
int o = 1; |
for (i = 0; i < func[f]->nfdeps; i++) |
if (!func[f]->fdeps[i]->tmp) {o = 0; break;} |
if (o) { |
func[f]->tmp = 1; |
change = 1; |
} |
} |
} while (change); |
|
change = 0; |
for (f = 0; f < MAX_FUNCS; f++) if (func[f] && !func[f]->tmp) { |
free_func (func[f]); |
func[f] = NULL; |
change = 1; |
} |
if (change) goto restart; |
} |
} |
|
void main_cuc (char *filename) |
{ |
int i, j; |
char tmp1[256]; |
char filename_cut[256]; |
#if 0 /* Select prefix, based on binary program name */ |
for (i = 0; i < sizeof (filename_cut); i++) { |
if (isalpha(filename[i])) filename_cut[i] = filename[i]; |
else { |
filename_cut[i] = '\0'; |
break; |
} |
} |
#else |
strcpy (filename_cut, "cu"); |
#endif |
|
PRINTF ("Entering OpenRISC Custom Unit Compiler command prompt\n"); |
PRINTF ("Using profile file \"%s\" and memory profile file \"%s\".\n", config.sim.prof_fn, config.sim.mprof_fn); |
sprintf (tmp1, "%s.log", filename_cut); |
PRINTF ("Analyzing. (log file \"%s\").\n", tmp1); |
assert (flog = fopen (tmp1, "wt+")); |
|
/* Loads in the specified timings table */ |
PRINTF ("Using timings from \"%s\" at %s\n",config.cuc.timings_fn, |
generate_time_pretty (tmp1, config.sim.clkcycle_ps)); |
load_timing_table (config.cuc.timings_fn); |
runtime.cuc.cycle_duration = 1000. * config.sim.clkcycle_ps; |
PRINTF ("Multicycle logic %s, bursts %s, %s memory order.\n", |
config.cuc.no_multicycle ? "OFF" : "ON", config.cuc.enable_bursts ? "ON" : "OFF", |
config.cuc.memory_order == MO_NONE ? "no" : config.cuc.memory_order == MO_WEAK ? "weak" : |
config.cuc.memory_order == MO_STRONG ? "strong" : "exact"); |
|
prof_set (1, 0); |
assert (prof_acquire (config.sim.prof_fn) == 0); |
|
if (config.cuc.calling_convention) |
PRINTF ("Assuming OpenRISC standard calling convention.\n"); |
|
/* Try all functions except "total" */ |
for (i = 0; i < prof_nfuncs - 1; i++) { |
long orig_time; |
unsigned long start_addr, end_addr; |
orig_time = prof_func[i].cum_cycles; |
start_addr = prof_func[i].addr; |
|
/* Extract the function from the binary */ |
sprintf (tmp1, "%s.bin", prof_func[i].name); |
end_addr = extract_function (tmp1, start_addr); |
|
log ("Testing function %s (%08x - %08x)\n", prof_func[i].name, start_addr, end_addr); |
PRINTF ("Testing function %s (%08x - %08x)\n", prof_func[i].name, start_addr, end_addr); |
func[i] = analyse_function (prof_func[i].name, orig_time, start_addr, |
end_addr, config.cuc.memory_order, prof_func[i].calls); |
func_v[i] = 0; |
} |
set_func_deps (); |
|
while (1) { |
char *s; |
wait_command: |
PRINTF ("(cuc) "); |
fflush (stdout); |
wait_command_empty: |
s = fgets(tmp1, sizeof tmp1, stdin); |
usleep (100); |
if (!s) goto wait_command_empty; |
for (s = tmp1; *s != '\0' && *s != '\n' && *s != '\r'; s++); |
*s = '\0'; |
|
/* quit command */ |
if (strcmp (tmp1, "q") == 0 || strcmp (tmp1, "quit") == 0) { |
/* Delete temporary files */ |
for (i = 0; i < prof_nfuncs - 1; i++) { |
sprintf (tmp1, "%s.bin", prof_func[i].name); |
log ("Deleting temporary file %s %s\n", tmp1, remove (tmp1) ? "FAILED" : "OK"); |
sprintf (tmp1, "%s.bin.bb", prof_func[i].name); |
log ("Deleting temporary file %s %s\n", tmp1, remove (tmp1) ? "FAILED" : "OK"); |
} |
break; |
|
/* profile command */ |
} else if (strcmp (tmp1, "p") == 0 || strcmp (tmp1, "profile") == 0) { |
int ntime = 0; |
int size = 0; |
PRINTF ("-----------------------------------------------------------------------------\n"); |
PRINTF ("|function name |calls|avg cycles |old%| max. f. | impr. f.| options |\n"); |
PRINTF ("|--------------------+-----+------------+----+----------|---------+---------|\n"); |
for (j = 0; j < prof_nfuncs; j++) { |
int bestcyc = 0, besti = 0; |
char tmp[100]; |
for (i = 0; i < prof_nfuncs; i++) |
if (prof_func[i].cum_cycles > bestcyc) { |
bestcyc = prof_func[i].cum_cycles; |
besti = i; |
} |
i = besti; |
PRINTF ("|%-20s|%5i|%12.1f|%3.0f%%| ", |
strstrip (tmp, prof_func[i].name, 20), prof_func[i].calls, |
((double)prof_func[i].cum_cycles / prof_func[i].calls), |
(100. * prof_func[i].cum_cycles / prof_cycles)); |
if (func[i]) { |
double f = 1.0; |
if (func_v[i]) { |
int nt = calc_cycles (func[i]); |
int s = calc_size (func[i]); |
f = 1. * func[i]->orig_time / nt; |
ntime += nt; |
size += s; |
} else ntime += prof_func[i].cum_cycles; |
PRINTF ("%8.1f |%8.1f | %-8s|\n", 1.f * prof_func[i].cum_cycles |
/ func[i]->timings.new_time, f, format_func_options (tmp, func[i])); |
} else { |
PRINTF (" N/A | N/A | N/A |\n"); |
ntime += prof_func[i].cum_cycles; |
} |
prof_func[i].cum_cycles = -prof_func[i].cum_cycles; |
} |
for (i = 0; i < prof_nfuncs; i++) |
prof_func[i].cum_cycles = -prof_func[i].cum_cycles; |
PRINTF ("-----------------------------------------------------------------------------\n"); |
PRINTF ("Total %i cycles (was %i), total added gates = %i. Speed factor %.1f\n", |
ntime, prof_cycles, size, 1. * prof_cycles / ntime); |
|
/* debug command */ |
} else if (strncmp (tmp1, "d", 1) == 0 || strncmp (tmp1, "debug", 5) == 0) { |
sscanf (tmp1, "%*s %i", &cuc_debug); |
if (cuc_debug < 0) cuc_debug = 0; |
if (cuc_debug > 9) cuc_debug = 9; |
|
/* generate command */ |
} else if (strcmp (tmp1, "g") == 0 || strcmp (tmp1, "generate") == 0) { |
/* check for function dependencies */ |
for (i = 0; i < prof_nfuncs; i++) |
if (func[i]) func[i]->tmp = func_v[i]; |
for (i = 0; i < prof_nfuncs; i++) if (func[i]) |
for (j = 0; j < func[i]->nfdeps; j++) |
if (!func[i]->fdeps[j] || !func[i]->fdeps[j]->tmp) { |
PRINTF ("Function %s must be selected for translation (required by %s)\n", |
prof_func[j].name, prof_func[i].name); |
goto wait_command; |
} |
for (i = 0; i < prof_nfuncs; i++) |
if (func[i] && func_v[i]) generate_function (func[i], prof_func[i].name, filename_cut); |
generate_main (prof_nfuncs, func, filename_cut); |
|
/* list command */ |
} else if (strcmp (tmp1, "l") == 0 || strcmp (tmp1, "list") == 0) { |
/* check for function dependencies */ |
for (i = 0; i < prof_nfuncs; i++) |
if (func_v[i]) { |
PRINTF ("%s\n", prof_func[j].name); |
} |
|
/* selectall command */ |
} else if (strcmp (tmp1, "sa") == 0 || strcmp (tmp1, "selectall") == 0) { |
char tmp[50], ch; |
int p, o, b, f; |
for (f = 0; f < prof_nfuncs; f++) if (func[f]) { |
func_v[f] = 1; |
PRINTF ("Function %s selected for translation.\n", prof_func[f].name); |
} |
|
/* select command */ |
} else if (strncmp (tmp1, "s", 1) == 0 || strncmp (tmp1, "select", 6) == 0) { |
char tmp[50], ch; |
int p, o, b, f; |
p = sscanf (tmp1, "%*s %s %i%c", tmp, &b, &ch); |
if (p < 1) PRINTF ("Invalid parameters.\n"); |
else { |
/* Check if we have valid option */ |
for (f = 0; f < prof_nfuncs; f++) |
if (strcmp (prof_func[f].name, tmp) == 0 && func[f]) break; |
if (f < prof_nfuncs) { |
if (p == 1) { |
if (func[f]) { |
func_v[f] = 1; |
PRINTF ("Function %s selected for translation.\n", prof_func[f].name); |
} else PRINTF ("Function %s not suitable for translation.\n", prof_func[f].name); |
} else { |
if (!func_v[f]) |
PRINTF ("Function %s not yet selected for translation.\n", prof_func[f].name); |
if (p < 3) goto invalid_option; |
for (o = 0; option_char[o] != '\0' && option_char[o] != ch; o++); |
if (!option_char[o]) goto invalid_option; |
if (b < 0 || b >= func[f]->num_bb) goto invalid_option; |
if (o < 0 || o >= func[f]->bb[b].ntim) goto invalid_option; |
|
/* select an option */ |
func[f]->bb[b].selected_tim = o; |
if (func[f]->bb[b].tim[o].nshared) { |
PRINTF ("Option has shared instructions: "); |
print_shared (func[f], func[f]->bb[b].tim[o].shared, func[f]->bb[b].tim[o].nshared); |
PRINTF ("\n"); |
} |
goto wait_command; |
invalid_option: |
PRINTF ("Invalid option.\n"); |
} |
} else PRINTF ("Invalid function.\n"); |
} |
|
/* unselect command */ |
} else if (strncmp (tmp1, "u", 1) == 0 || strncmp (tmp1, "unselect", 8) == 0) { |
char tmp[50], ch; |
int p, o, b, f; |
p = sscanf (tmp1, "%*s %s %i%c", tmp, &b, &ch); |
if (p < 1) PRINTF ("Invalid parameters.\n"); |
else { |
/* Check if we have valid option */ |
for (f = 0; f < prof_nfuncs; f++) |
if (strcmp (prof_func[f].name, tmp) == 0 && func[f]) break; |
if (f < prof_nfuncs) { |
if (p == 1) { |
if (func[f]) { |
func_v[f] = 0; |
PRINTF ("Function %s unselected for translation.\n", prof_func[f].name); |
} else PRINTF ("Function %s not suitable for translation.\n", prof_func[f].name); |
} else { |
if (p < 3) goto invalid_option; |
for (o = 0; option_char[o] != '\0' && option_char[o] != ch; o++); |
if (!option_char[o]) goto invalid_option; |
if (b < 0 || b >= func[f]->num_bb) goto invalid_option; |
if (o < 0 || o >= func[f]->bb[b].ntim) goto invalid_option; |
|
/* select an option */ |
func[f]->bb[b].selected_tim = -1; |
} |
} else PRINTF ("Invalid function.\n"); |
} |
|
/* options command */ |
} else if (strcmp (tmp1, "o") == 0 || strcmp (tmp1, "options") == 0) { |
int any = 0; |
PRINTF ("Available options:\n"); |
for (i = 0; i < prof_nfuncs; i++) |
if (func[i]) { |
options_cmd (i, func[i]); |
any = 1; |
} |
if (any) PRINTF ("-----------------------------------------------------------------------------\n"); |
else PRINTF ("Sorry. No available options.\n"); |
|
/* Ignore empty string */ |
} else if (strcmp (tmp1, "") == 0) { |
|
/* help command */ |
} else { |
if (strcmp (tmp1, "h") != 0 && strcmp (tmp1, "help") != 0) |
PRINTF ("Unknown command.\n"); |
PRINTF ("OpenRISC Custom Unit Compiler command prompt\n"); |
PRINTF ("Available commands:\n"); |
PRINTF (" h | help displays this help\n"); |
PRINTF (" q | quit returns to or1ksim prompt\n"); |
PRINTF (" p | profile displays function profiling\n"); |
PRINTF (" d | debug # sets debug level (0-9)\n"); |
PRINTF (" o | options displays available options\n"); |
PRINTF (" s | select func [option] selects an option/function\n"); |
PRINTF (" u | unselect func [option] unselects an option/function\n"); |
PRINTF (" g | generate generates verilog file\n"); |
PRINTF (" l | list displays selected functions\n"); |
} |
} |
|
/* Dispose memory */ |
for (i = 0; i < prof_nfuncs -1; i++) |
if (func[i]) free_func (func[i]); |
|
fclose (flog); |
} |
|
/* cuc.c -- OpenRISC Custom Unit Compiler |
* Copyright (C) 2002 Marko Mlinar, markom@opencores.org |
* |
* This file is part of OpenRISC 1000 Architectural Simulator. |
* |
* This program is free software; you can redistribute it and/or modify |
* it under the terms of the GNU General Public License as published by |
* the Free Software Foundation; either version 2 of the License, or |
* (at your option) any later version. |
* |
* This program is distributed in the hope that it will be useful, |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
* GNU General Public License for more details. |
* |
* You should have received a copy of the GNU General Public License |
* along with this program; if not, write to the Free Software |
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ |
|
/* Main file, including code optimization and command prompt */ |
|
#include <stdio.h> |
#include <stdlib.h> |
#include <stdarg.h> |
#include <assert.h> |
#include <ctype.h> |
#include "sim-config.h" |
#include "cuc.h" |
#include "insn.h" |
#include "profiler.h" |
#include "opcode/or32.h" |
#include "parse.h" |
|
FILE *flog; |
int cuc_debug = 0; |
|
/* Last used registers by software convention */ |
/* Note that r11 is caller saved register, and we can destroy it. |
Due to CUC architecture we must always return something, even garbage (so that |
caller knows, we are finished, when we send acknowledge). |
In case r11 was not used (trivial register assignment) we will remove it later, |
but if we assigned a value to it, it must not be removed, so caller_saved[11] = 0 */ |
const int caller_saved[MAX_REGS] = { |
0, 0, 0, 1, 1, 1, 1, 1, |
1, 1, 0, 0, 0, 1, 0, 1, |
0, 1, 0, 1, 0, 1, 0, 1, |
0, 1, 0, 1, 0, 1, 0, 1, |
1, 1}; |
|
/* returns log2(x) */ |
/* Call this log2_int, because there is a library function named log2 */ |
int log2_int (unsigned long x) |
{ |
int c = 0; |
assert (x >= 0); |
if (!x) return 0; /* not by the book, but practical */ |
while (x != 1) x >>= 1, c++; |
return c; |
} |
|
/* Does all known instruction optimizations */ |
void cuc_optimize (cuc_func *func) |
{ |
int modified = 0; |
int first = 1; |
log ("Optimizing.\n"); |
do { |
modified = 0; |
clean_deps (func); |
if (cuc_debug >= 6) print_cuc_bb (func, "AFTER_CLEAN_DEPS"); |
if (optimize_cmovs (func)) { |
if (cuc_debug >= 6) print_cuc_bb (func, "AFTER_OPT_CMOVS"); |
modified = 1; |
} |
if (cuc_debug) cuc_check (func); |
if (optimize_tree (func)) { |
if (cuc_debug >= 6) print_cuc_bb (func, "AFTER_OPT_TREE1"); |
modified = 1; |
} |
if (remove_nops (func)) { |
if (cuc_debug >= 6) print_cuc_bb (func, "NO_NOPS"); |
modified = 1; |
} |
if (cuc_debug) cuc_check (func); |
if (remove_dead (func)) { |
if (cuc_debug >= 5) print_cuc_bb (func, "AFTER_DEAD"); |
modified = 1; |
} |
if (cuc_debug) cuc_check (func); |
if (cse (func)) { |
log ("Common subexpression elimination.\n"); |
if (cuc_debug >= 3) print_cuc_bb (func, "AFTER_CSE"); |
modified = 1; |
} |
if (first) { |
insert_conditional_facts (func); |
if (cuc_debug >= 3) print_cuc_bb (func, "AFTER_COND_FACT"); |
if (cuc_debug) cuc_check (func); |
first = 0; |
} |
if (optimize_bb (func)) { |
if (cuc_debug >= 5) print_cuc_bb (func, "AFTER_OPT_BB"); |
modified = 1; |
} |
if (cuc_debug) cuc_check (func); |
if (remove_nops (func)) { |
if (cuc_debug >= 6) print_cuc_bb (func, "NO_NOPS"); |
modified = 1; |
} |
if (remove_dead_bb (func)) { |
if (cuc_debug >= 5) print_cuc_bb (func, "AFTER_DEAD_BB"); |
modified = 1; |
} |
if (remove_trivial_regs (func)) { |
if (cuc_debug >= 2) print_cuc_bb (func, "AFTER_TRIVIAL"); |
modified = 1; |
} |
if (remove_nops (func)) { |
if (cuc_debug >= 6) print_cuc_bb (func, "NO_NOPS"); |
modified = 1; |
} |
add_memory_dep (func, func->memory_order); |
if (cuc_debug >= 7) print_cuc_bb (func, "AFTER_MEMORY_DEP"); |
add_data_dep (func); |
if (cuc_debug >= 8) print_cuc_bb (func, "AFTER_DATA_DEP"); |
if (schedule_memory (func, func->memory_order)) { |
if (cuc_debug >= 7) print_cuc_bb (func, "AFTER_SCHEDULE_MEM"); |
modified = 1; |
} |
} while (modified); |
set_io (func); |
#if 0 |
detect_max_values (func); |
if (cuc_debug >= 5) print_cuc_bb (func, "AFTER_MAX_VALUES"); |
#endif |
} |
|
/* Pre/unrolls basic block and optimizes it */ |
cuc_timings *preunroll_bb (char *bb_filename, cuc_func *f, cuc_timings *timings, int b, int i, int j) |
{ |
cuc_func *func; |
cucdebug (2, "BB%i unroll %i times preroll %i times\n", b, j, i); |
log ("BB%i unroll %i times preroll %i times\n", b, j, i); |
func = preunroll_loop (f, b, i, j, bb_filename); |
if (cuc_debug >= 2) print_cuc_bb (func, "AFTER_PREUNROLL"); |
cuc_optimize (func); |
analyse_timings (func, timings); |
|
cucdebug (2, "new_time = %i, old_time = %i, size = %f\n", |
timings->new_time, func->orig_time, timings->size); |
log ("new time = %icyc, old_time = %icyc, size = %.0f gates\n", |
timings->new_time, func->orig_time, timings->size); |
//output_verilog (func, argv[1]); |
free_func (func); |
timings->b = b; |
timings->unroll = j; |
timings->preroll = i; |
timings->nshared = 0; |
return timings; |
} |
|
/* Simple comparison function */ |
int tim_comp (cuc_timings *a, cuc_timings *b) |
{ |
if (a->new_time < b->new_time) return -1; |
else if (a->new_time > b->new_time) return 1; |
else return 0; |
} |
|
/* Analyses function; done when cuc command is entered in (sim) prompt */ |
cuc_func *analyse_function (char *module_name, long orig_time, |
unsigned long start_addr, unsigned long end_addr, |
int memory_order, int num_runs) |
{ |
cuc_timings timings; |
cuc_func *func = (cuc_func *) malloc (sizeof (cuc_func)); |
cuc_func *saved; |
int b, i, j; |
char tmp1[256]; |
char tmp2[256]; |
|
func->orig_time = orig_time; |
func->start_addr = start_addr; |
func->end_addr = end_addr; |
func->memory_order = memory_order; |
func->nfdeps = 0; |
func->fdeps = NULL; |
func->num_runs = num_runs; |
|
sprintf (tmp1, "%s.bin", module_name); |
cucdebug (2, "Loading %s.bin\n", module_name); |
if (cuc_load (tmp1)) { |
free (func); |
return NULL; |
} |
|
log ("Detecting basic blocks\n"); |
detect_bb (func); |
if (cuc_debug >= 2) print_cuc_insns ("WITH_BB_LIMITS", 0); |
|
//sprintf (tmp1, "%s.bin.mp", module_name); |
sprintf (tmp2, "%s.bin.bb", module_name); |
generate_bb_seq (func, config.sim.mprof_fn, tmp2); |
log ("Assuming %i clk cycle load (%i cyc burst)\n", runtime.cuc.mdelay[0], runtime.cuc.mdelay[2]); |
log ("Assuming %i clk cycle store (%i cyc burst)\n", runtime.cuc.mdelay[1], runtime.cuc.mdelay[3]); |
|
build_bb (func); |
if (cuc_debug >= 5) print_cuc_bb (func, "AFTER_BUILD_BB"); |
reg_dep (func); |
|
log ("Detecting dependencies\n"); |
if (cuc_debug >= 2) print_cuc_bb (func, "AFTER_REG_DEP"); |
cuc_optimize (func); |
|
#if 0 |
csm (func); |
#endif |
assert (saved = dup_func (func)); |
|
timings.preroll = timings.unroll = 1; |
timings.nshared = 0; |
|
add_latches (func); |
if (cuc_debug >= 1) print_cuc_bb (func, "AFTER_LATCHES"); |
analyse_timings (func, &timings); |
|
free_func (func); |
log ("Base option: pre%i,un%i,sha%i: %icyc %.1f\n", |
timings.preroll, timings.unroll, timings.nshared, timings.new_time, timings.size); |
saved->timings = timings; |
|
#if 1 |
/* detect and unroll simple loops */ |
for (b = 0; b < saved->num_bb; b++) { |
cuc_timings t[MAX_UNROLL * MAX_PREROLL]; |
cuc_timings *ut; |
cuc_timings *cut = &t[0]; |
int nt = 1; |
double csize; |
saved->bb[b].selected_tim = -1; |
|
/* Is it a loop? */ |
if (saved->bb[b].next[0] != b && saved->bb[b].next[1] != b) continue; |
log ("Found loop at BB%x. Trying to unroll.\n", b); |
t[0] = timings; |
t[0].b = b; |
t[0].preroll = 1; |
t[0].unroll = 1; |
t[0].nshared = 0; |
|
sprintf (tmp1, "%s.bin.bb", module_name); |
i = 1; |
do { |
cuc_timings *pt; |
cuc_timings *cpt = cut; |
j = 1; |
|
do { |
pt = cpt; |
cpt = preunroll_bb (tmp1, saved, &t[nt++], b, ++j, i); |
} while (j <= MAX_PREROLL && pt->new_time > cpt->new_time); |
i++; |
ut = cut; |
cut = preunroll_bb (tmp1, saved, &t[nt++], b, 1, i); |
} while (i <= MAX_UNROLL && ut->new_time > cut->new_time); |
|
/* Sort the timings */ |
#if 0 |
if (cuc_debug >= 3) |
for (i = 0; i < nt; i++) PRINTF ("%i:%i,%i: %icyc\n", |
t[i].b, t[i].preroll, t[i].unroll, t[i].new_time); |
#endif |
|
qsort (t, nt, sizeof (cuc_timings), (int (*)(const void *, const void *))tim_comp); |
|
/* Delete timings, that have worst time and bigger size than other */ |
j = 1; |
csize = t[0].size; |
for (i = 1; i < nt; i++) |
if (t[i].size < csize) t[j++] = t[i]; |
nt = j; |
|
cucdebug (1, "Available options\n"); |
for (i = 0; i < nt; i++) cucdebug (1, "%i:%i,%i: %icyc %.1f\n", |
t[i].b, t[i].preroll, t[i].unroll, t[i].new_time, t[i].size); |
/* Add results from CSM */ |
j = nt; |
for (i = 0; i < saved->bb[b].ntim; i++) { |
int i1; |
for (i1 = 0; i1 < nt; i1++) { |
t[j] = t[i1]; |
t[j].size += saved->bb[b].tim[i].size - timings.size; |
t[j].new_time += saved->bb[b].tim[i].new_time - timings.new_time; |
t[j].nshared = saved->bb[b].tim[i].nshared; |
t[j].shared = saved->bb[b].tim[i].shared; |
if (++j >= MAX_UNROLL * MAX_PREROLL) goto full; |
} |
} |
|
full: |
nt = j; |
|
cucdebug (1, "Available options:\n"); |
for (i = 0; i < nt; i++) cucdebug (1, "%i:%i,%i: %icyc %.1f\n", |
t[i].b, t[i].preroll, t[i].unroll, t[i].new_time, t[i].size); |
|
/* Sort again with new timings added */ |
qsort (t, nt, sizeof (cuc_timings), (int (*)(const void *, const void *))tim_comp); |
|
/* Delete timings, that have worst time and bigger size than other */ |
j = 1; |
csize = t[0].size; |
for (i = 1; i < nt; i++) |
if (t[i].size < csize) t[j++] = t[i]; |
nt = j; |
|
cucdebug (1, "Available options:\n"); |
for (i = 0; i < nt; i++) cucdebug (1, "%i:%i,%i: %icyc %.1f\n", |
t[i].b, t[i].preroll, t[i].unroll, t[i].new_time, t[i].size); |
|
if (saved->bb[b].ntim) free (saved->bb[b].tim); |
saved->bb[b].ntim = nt; |
assert (saved->bb[b].tim = (cuc_timings *) malloc (sizeof (cuc_timings) * nt)); |
|
/* Copy options in reverse order -- smallest first */ |
for (i = 0; i < nt; i++) saved->bb[b].tim[i] = t[nt - 1 - i]; |
|
log ("Available options:\n"); |
for (i = 0; i < saved->bb[b].ntim; i++) { |
log ("%i:pre%i,un%i,sha%i: %icyc %.1f\n", |
saved->bb[b].tim[i].b, saved->bb[b].tim[i].preroll, saved->bb[b].tim[i].unroll, |
saved->bb[b].tim[i].nshared, saved->bb[b].tim[i].new_time, saved->bb[b].tim[i].size); |
} |
} |
#endif |
return saved; |
} |
|
/* Utility option formatting functions */ |
static const char *option_char = "?abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; |
|
/*static */char *gen_option (char *s, int bb_no, int f_opt) |
{ |
if (bb_no >= 0) sprintf (s, "%i", bb_no); |
assert (f_opt <= strlen (option_char)); |
sprintf (s, "%s%c", s, option_char[f_opt]); |
return s; |
} |
|
/*static */void print_option (int bb_no, int f_opt) |
{ |
char tmp1[10]; |
char tmp2[10]; |
sprintf (tmp2, "%s", gen_option (tmp1, bb_no, f_opt)); |
PRINTF ("%3s", tmp2); |
} |
|
static char *format_func_options (char *s, cuc_func *f) |
{ |
int b, first = 1; |
*s = '\0'; |
for (b = 0; b < f->num_bb; b++) |
if (f->bb[b].selected_tim >= 0) { |
char tmp[10]; |
sprintf (s, "%s%s%s", s, first ? "" : ",", gen_option (tmp, b, f->bb[b].selected_tim)); |
first = 0; |
} |
return s; |
} |
|
static void options_cmd (int func_no, cuc_func *f) |
{ |
int b, i; |
char tmp[30]; |
char *name = prof_func[func_no].name; |
PRINTF ("-----------------------------------------------------------------------------\n"); |
PRINTF ("|%-28s|pre/unrolled|shared| time | gates |old_time|\n", |
strstrip (tmp, name, 28)); |
PRINTF ("| BASE |%4i / %4i | %4i |%8i|%8.f|%8i|\n", 1, 1, 0, |
f->timings.new_time, f->timings.size, f->orig_time); |
for (b = 0; b < f->num_bb; b++) { |
/* Print out results */ |
for (i = 1; i < f->bb[b].ntim; i++) { /* First one is base option */ |
int time = f->bb[b].tim[i].new_time - f->timings.new_time; |
double size = f->bb[b].tim[i].size - f->timings.size; |
PRINTF ("| "); |
print_option (b, i); |
PRINTF (" |%4i / %4i | %4i |%+8i|%+8.f| |\n", |
f->bb[b].tim[i].preroll, f->bb[b].tim[i].unroll, f->bb[b].tim[i].nshared, |
time, size); |
} |
} |
} |
|
/* Generates a function, based on specified parameters */ |
cuc_func *generate_function (cuc_func *rf, char *name, char *cut_filename) |
{ |
int b, i, j; |
char tmp[256]; |
cuc_timings tt; |
cuc_func *f; |
assert (f = dup_func (rf)); |
|
if (cuc_debug >= 2) print_cuc_bb (f, "BEFORE_GENERATE"); |
log ("Generating function %s.\n", name); |
PRINTF ("Generating function %s.\n", name); |
|
format_func_options (tmp, rf); |
if (strlen (tmp)) PRINTF ("Applying options: %s\n", tmp); |
else PRINTF ("Using basic options.\n"); |
|
/* Generate function as specified by options */ |
for (b = 0; b < f->num_bb; b++) { |
cuc_timings *st; |
if (rf->bb[b].selected_tim < 0) continue; |
st = &rf->bb[b].tim[rf->bb[b].selected_tim]; |
sprintf (tmp, "%s.bin.bb", name); |
preunroll_bb (&tmp[0], f, &tt, b, st->preroll, st->unroll); |
if (cuc_debug >= 1) print_cuc_bb (f, "AFTER_PREUNROLL"); |
} |
for (b = 0; b < f->num_bb; b++) { |
cuc_timings *st; |
if (rf->bb[b].selected_tim < 0) continue; |
st = &rf->bb[b].tim[rf->bb[b].selected_tim]; |
if (!st->nshared) continue; |
assert (0); |
//csm_gen (f, rf, st->nshared, st->shared); |
} |
add_latches (f); |
if (cuc_debug >= 1) print_cuc_bb (f, "AFTER_LATCHES"); |
analyse_timings (f, &tt); |
|
sprintf (tmp, "%s%s", cut_filename, name); |
output_verilog (f, tmp, name); |
return f; |
} |
|
/* Calculates required time, based on selected options */ |
int calc_cycles (cuc_func *f) |
{ |
int b, i, ntime = f->timings.new_time; |
for (b = 0; b < f->num_bb; b++) |
if (f->bb[b].selected_tim >= 0) { |
assert (f->bb[b].selected_tim < f->bb[b].ntim); |
ntime += f->bb[b].tim[f->bb[b].selected_tim].new_time - f->timings.new_time; |
} |
return ntime; |
} |
|
/* Calculates required size, based on selected options */ |
double calc_size (cuc_func *f) |
{ |
int b, i; |
double size = f->timings.size; |
for (b = 0; b < f->num_bb; b++) |
if (f->bb[b].selected_tim >= 0) { |
assert (f->bb[b].selected_tim < f->bb[b].ntim); |
size += f->bb[b].tim[f->bb[b].selected_tim].size - f->timings.size; |
} |
return size; |
} |
|
/* Dumps specified function to file (hex) */ |
unsigned long extract_function (char *out_fn, unsigned long start_addr) |
{ |
FILE *fo; |
unsigned long a = start_addr; |
int x = 0; |
assert (fo = fopen (out_fn, "wt+")); |
|
do { |
unsigned long d = evalsim_mem32 (a); |
int index = insn_decode (d); |
assert (index >= 0); |
if (x) x++; |
if (strcmp (insn_name (index), "l.jr") == 0) x = 1; |
a += 4; |
fprintf (fo, "%08x\n", d); |
} while (x < 2); |
|
fclose (fo); |
return a - 4; |
} |
|
static cuc_func *func[MAX_FUNCS]; |
static int func_v[MAX_FUNCS]; |
|
/* Detects function dependencies and removes */ |
static void set_func_deps () |
{ |
int f, b, i, j; |
restart: |
for (f = 0; f < prof_nfuncs - 1; f++) if (func[f]) { |
int fused[MAX_FUNCS] = {0}; |
int c; |
for (b = 0; b < func[f]->num_bb; b++) |
for (i = 0; i < func[f]->bb[b].ninsn; i++) { |
cuc_insn *ii = &func[f]->bb[b].insn[i]; |
if (ii->index == II_CALL) { |
assert (ii->opt[0] == OPT_CONST); |
for (j = 0; j < prof_nfuncs - 1; j++) |
if (func[j] && func[j]->start_addr == ii->op[0]) break; |
if (j >= prof_nfuncs - 1) { |
log ("%s is calling unknown function, address %08x\n", |
prof_func[f].name, ii->op[0]); |
debug (1, "%s is calling unknown function, address %08x\n", |
prof_func[f].name, ii->op[0]); |
free_func (func[f]); |
func[f] = NULL; |
goto restart; |
} else if (f == j) { |
log ("%s is recursive, ignoring\n", prof_func[f].name); |
debug (1, "%s is recursive, ignoring\n", prof_func[f].name); |
free_func (func[f]); |
func[f] = NULL; |
goto restart; |
} else fused[j]++; |
} |
} |
for (i = 0; i < MAX_FUNCS; i++) if (fused[i]) c++; |
if (func[f]->nfdeps) free (func[f]->fdeps); |
func[f]->nfdeps = c; |
func[f]->fdeps = (cuc_func **) malloc (sizeof (cuc_func *) * c); |
for (i = 0, j = 0; i < MAX_FUNCS; i++) |
if (fused[i]) func[f]->fdeps[j++] = func[i]; |
} |
|
/* Detect loops */ |
{ |
int change; |
for (f = 0; f < MAX_FUNCS; f++) if (func[f]) func[f]->tmp = 0; |
do { |
change = 0; |
for (f = 0; f < MAX_FUNCS; f++) if (func[f] && !func[f]->tmp) { |
int o = 1; |
for (i = 0; i < func[f]->nfdeps; i++) |
if (!func[f]->fdeps[i]->tmp) {o = 0; break;} |
if (o) { |
func[f]->tmp = 1; |
change = 1; |
} |
} |
} while (change); |
|
change = 0; |
for (f = 0; f < MAX_FUNCS; f++) if (func[f] && !func[f]->tmp) { |
free_func (func[f]); |
func[f] = NULL; |
change = 1; |
} |
if (change) goto restart; |
} |
} |
|
void main_cuc (char *filename) |
{ |
int i, j; |
char tmp1[256]; |
char filename_cut[256]; |
#if 0 /* Select prefix, based on binary program name */ |
for (i = 0; i < sizeof (filename_cut); i++) { |
if (isalpha(filename[i])) filename_cut[i] = filename[i]; |
else { |
filename_cut[i] = '\0'; |
break; |
} |
} |
#else |
strcpy (filename_cut, "cu"); |
#endif |
|
PRINTF ("Entering OpenRISC Custom Unit Compiler command prompt\n"); |
PRINTF ("Using profile file \"%s\" and memory profile file \"%s\".\n", config.sim.prof_fn, config.sim.mprof_fn); |
sprintf (tmp1, "%s.log", filename_cut); |
PRINTF ("Analyzing. (log file \"%s\").\n", tmp1); |
assert (flog = fopen (tmp1, "wt+")); |
|
/* Loads in the specified timings table */ |
PRINTF ("Using timings from \"%s\" at %s\n",config.cuc.timings_fn, |
generate_time_pretty (tmp1, config.sim.clkcycle_ps)); |
load_timing_table (config.cuc.timings_fn); |
runtime.cuc.cycle_duration = 1000. * config.sim.clkcycle_ps; |
PRINTF ("Multicycle logic %s, bursts %s, %s memory order.\n", |
config.cuc.no_multicycle ? "OFF" : "ON", config.cuc.enable_bursts ? "ON" : "OFF", |
config.cuc.memory_order == MO_NONE ? "no" : config.cuc.memory_order == MO_WEAK ? "weak" : |
config.cuc.memory_order == MO_STRONG ? "strong" : "exact"); |
|
prof_set (1, 0); |
assert (prof_acquire (config.sim.prof_fn) == 0); |
|
if (config.cuc.calling_convention) |
PRINTF ("Assuming OpenRISC standard calling convention.\n"); |
|
/* Try all functions except "total" */ |
for (i = 0; i < prof_nfuncs - 1; i++) { |
long orig_time; |
unsigned long start_addr, end_addr; |
orig_time = prof_func[i].cum_cycles; |
start_addr = prof_func[i].addr; |
|
/* Extract the function from the binary */ |
sprintf (tmp1, "%s.bin", prof_func[i].name); |
end_addr = extract_function (tmp1, start_addr); |
|
log ("Testing function %s (%08x - %08x)\n", prof_func[i].name, start_addr, end_addr); |
PRINTF ("Testing function %s (%08x - %08x)\n", prof_func[i].name, start_addr, end_addr); |
func[i] = analyse_function (prof_func[i].name, orig_time, start_addr, |
end_addr, config.cuc.memory_order, prof_func[i].calls); |
func_v[i] = 0; |
} |
set_func_deps (); |
|
while (1) { |
char *s; |
wait_command: |
PRINTF ("(cuc) "); |
fflush (stdout); |
wait_command_empty: |
s = fgets(tmp1, sizeof tmp1, stdin); |
usleep (100); |
if (!s) goto wait_command_empty; |
for (s = tmp1; *s != '\0' && *s != '\n' && *s != '\r'; s++); |
*s = '\0'; |
|
/* quit command */ |
if (strcmp (tmp1, "q") == 0 || strcmp (tmp1, "quit") == 0) { |
/* Delete temporary files */ |
for (i = 0; i < prof_nfuncs - 1; i++) { |
sprintf (tmp1, "%s.bin", prof_func[i].name); |
log ("Deleting temporary file %s %s\n", tmp1, remove (tmp1) ? "FAILED" : "OK"); |
sprintf (tmp1, "%s.bin.bb", prof_func[i].name); |
log ("Deleting temporary file %s %s\n", tmp1, remove (tmp1) ? "FAILED" : "OK"); |
} |
break; |
|
/* profile command */ |
} else if (strcmp (tmp1, "p") == 0 || strcmp (tmp1, "profile") == 0) { |
int ntime = 0; |
int size = 0; |
PRINTF ("-----------------------------------------------------------------------------\n"); |
PRINTF ("|function name |calls|avg cycles |old%| max. f. | impr. f.| options |\n"); |
PRINTF ("|--------------------+-----+------------+----+----------|---------+---------|\n"); |
for (j = 0; j < prof_nfuncs; j++) { |
int bestcyc = 0, besti = 0; |
char tmp[100]; |
for (i = 0; i < prof_nfuncs; i++) |
if (prof_func[i].cum_cycles > bestcyc) { |
bestcyc = prof_func[i].cum_cycles; |
besti = i; |
} |
i = besti; |
PRINTF ("|%-20s|%5i|%12.1f|%3.0f%%| ", |
strstrip (tmp, prof_func[i].name, 20), prof_func[i].calls, |
((double)prof_func[i].cum_cycles / prof_func[i].calls), |
(100. * prof_func[i].cum_cycles / prof_cycles)); |
if (func[i]) { |
double f = 1.0; |
if (func_v[i]) { |
int nt = calc_cycles (func[i]); |
int s = calc_size (func[i]); |
f = 1. * func[i]->orig_time / nt; |
ntime += nt; |
size += s; |
} else ntime += prof_func[i].cum_cycles; |
PRINTF ("%8.1f |%8.1f | %-8s|\n", 1.f * prof_func[i].cum_cycles |
/ func[i]->timings.new_time, f, format_func_options (tmp, func[i])); |
} else { |
PRINTF (" N/A | N/A | N/A |\n"); |
ntime += prof_func[i].cum_cycles; |
} |
prof_func[i].cum_cycles = -prof_func[i].cum_cycles; |
} |
for (i = 0; i < prof_nfuncs; i++) |
prof_func[i].cum_cycles = -prof_func[i].cum_cycles; |
PRINTF ("-----------------------------------------------------------------------------\n"); |
PRINTF ("Total %i cycles (was %i), total added gates = %i. Speed factor %.1f\n", |
ntime, prof_cycles, size, 1. * prof_cycles / ntime); |
|
/* debug command */ |
} else if (strncmp (tmp1, "d", 1) == 0 || strncmp (tmp1, "debug", 5) == 0) { |
sscanf (tmp1, "%*s %i", &cuc_debug); |
if (cuc_debug < 0) cuc_debug = 0; |
if (cuc_debug > 9) cuc_debug = 9; |
|
/* generate command */ |
} else if (strcmp (tmp1, "g") == 0 || strcmp (tmp1, "generate") == 0) { |
/* check for function dependencies */ |
for (i = 0; i < prof_nfuncs; i++) |
if (func[i]) func[i]->tmp = func_v[i]; |
for (i = 0; i < prof_nfuncs; i++) if (func[i]) |
for (j = 0; j < func[i]->nfdeps; j++) |
if (!func[i]->fdeps[j] || !func[i]->fdeps[j]->tmp) { |
PRINTF ("Function %s must be selected for translation (required by %s)\n", |
prof_func[j].name, prof_func[i].name); |
goto wait_command; |
} |
for (i = 0; i < prof_nfuncs; i++) |
if (func[i] && func_v[i]) generate_function (func[i], prof_func[i].name, filename_cut); |
generate_main (prof_nfuncs, func, filename_cut); |
|
/* list command */ |
} else if (strcmp (tmp1, "l") == 0 || strcmp (tmp1, "list") == 0) { |
/* check for function dependencies */ |
for (i = 0; i < prof_nfuncs; i++) |
if (func_v[i]) { |
PRINTF ("%s\n", prof_func[j].name); |
} |
|
/* selectall command */ |
} else if (strcmp (tmp1, "sa") == 0 || strcmp (tmp1, "selectall") == 0) { |
char tmp[50], ch; |
int p, o, b, f; |
for (f = 0; f < prof_nfuncs; f++) if (func[f]) { |
func_v[f] = 1; |
PRINTF ("Function %s selected for translation.\n", prof_func[f].name); |
} |
|
/* select command */ |
} else if (strncmp (tmp1, "s", 1) == 0 || strncmp (tmp1, "select", 6) == 0) { |
char tmp[50], ch; |
int p, o, b, f; |
p = sscanf (tmp1, "%*s %s %i%c", tmp, &b, &ch); |
if (p < 1) PRINTF ("Invalid parameters.\n"); |
else { |
/* Check if we have valid option */ |
for (f = 0; f < prof_nfuncs; f++) |
if (strcmp (prof_func[f].name, tmp) == 0 && func[f]) break; |
if (f < prof_nfuncs) { |
if (p == 1) { |
if (func[f]) { |
func_v[f] = 1; |
PRINTF ("Function %s selected for translation.\n", prof_func[f].name); |
} else PRINTF ("Function %s not suitable for translation.\n", prof_func[f].name); |
} else { |
if (!func_v[f]) |
PRINTF ("Function %s not yet selected for translation.\n", prof_func[f].name); |
if (p < 3) goto invalid_option; |
for (o = 0; option_char[o] != '\0' && option_char[o] != ch; o++); |
if (!option_char[o]) goto invalid_option; |
if (b < 0 || b >= func[f]->num_bb) goto invalid_option; |
if (o < 0 || o >= func[f]->bb[b].ntim) goto invalid_option; |
|
/* select an option */ |
func[f]->bb[b].selected_tim = o; |
if (func[f]->bb[b].tim[o].nshared) { |
PRINTF ("Option has shared instructions: "); |
print_shared (func[f], func[f]->bb[b].tim[o].shared, func[f]->bb[b].tim[o].nshared); |
PRINTF ("\n"); |
} |
goto wait_command; |
invalid_option: |
PRINTF ("Invalid option.\n"); |
} |
} else PRINTF ("Invalid function.\n"); |
} |
|
/* unselect command */ |
} else if (strncmp (tmp1, "u", 1) == 0 || strncmp (tmp1, "unselect", 8) == 0) { |
char tmp[50], ch; |
int p, o, b, f; |
p = sscanf (tmp1, "%*s %s %i%c", tmp, &b, &ch); |
if (p < 1) PRINTF ("Invalid parameters.\n"); |
else { |
/* Check if we have valid option */ |
for (f = 0; f < prof_nfuncs; f++) |
if (strcmp (prof_func[f].name, tmp) == 0 && func[f]) break; |
if (f < prof_nfuncs) { |
if (p == 1) { |
if (func[f]) { |
func_v[f] = 0; |
PRINTF ("Function %s unselected for translation.\n", prof_func[f].name); |
} else PRINTF ("Function %s not suitable for translation.\n", prof_func[f].name); |
} else { |
if (p < 3) goto invalid_option; |
for (o = 0; option_char[o] != '\0' && option_char[o] != ch; o++); |
if (!option_char[o]) goto invalid_option; |
if (b < 0 || b >= func[f]->num_bb) goto invalid_option; |
if (o < 0 || o >= func[f]->bb[b].ntim) goto invalid_option; |
|
/* select an option */ |
func[f]->bb[b].selected_tim = -1; |
} |
} else PRINTF ("Invalid function.\n"); |
} |
|
/* options command */ |
} else if (strcmp (tmp1, "o") == 0 || strcmp (tmp1, "options") == 0) { |
int any = 0; |
PRINTF ("Available options:\n"); |
for (i = 0; i < prof_nfuncs; i++) |
if (func[i]) { |
options_cmd (i, func[i]); |
any = 1; |
} |
if (any) PRINTF ("-----------------------------------------------------------------------------\n"); |
else PRINTF ("Sorry. No available options.\n"); |
|
/* Ignore empty string */ |
} else if (strcmp (tmp1, "") == 0) { |
|
/* help command */ |
} else { |
if (strcmp (tmp1, "h") != 0 && strcmp (tmp1, "help") != 0) |
PRINTF ("Unknown command.\n"); |
PRINTF ("OpenRISC Custom Unit Compiler command prompt\n"); |
PRINTF ("Available commands:\n"); |
PRINTF (" h | help displays this help\n"); |
PRINTF (" q | quit returns to or1ksim prompt\n"); |
PRINTF (" p | profile displays function profiling\n"); |
PRINTF (" d | debug # sets debug level (0-9)\n"); |
PRINTF (" o | options displays available options\n"); |
PRINTF (" s | select func [option] selects an option/function\n"); |
PRINTF (" u | unselect func [option] unselects an option/function\n"); |
PRINTF (" g | generate generates verilog file\n"); |
PRINTF (" l | list displays selected functions\n"); |
} |
} |
|
/* Dispose memory */ |
for (i = 0; i < prof_nfuncs -1; i++) |
if (func[i]) free_func (func[i]); |
|
fclose (flog); |
} |
|
/trunk/or1ksim/peripheral/channels/file.c
19,12 → 19,18
along with this program; if not, write to the Free Software |
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ |
|
#if HAVE_CONFIG_H |
#include <config.h> |
#endif |
|
#define _GNU_SOURCE /* for strndup */ |
|
#include <sys/types.h> /* open() */ |
#include <sys/stat.h> /* open() */ |
#include <fcntl.h> /* open() */ |
#include <malloc.h> /* calloc(), free() */ |
#if HAVE_MALLOC_H |
#include <malloc.h> /* calloc, free */ |
#endif |
#include <string.h> /* strndup(), strchr() */ |
#include <errno.h> /* errno */ |
#include <unistd.h> /* close() */ |
/trunk/or1ksim/peripheral/channels/xterm.c
26,6 → 26,10
* They are suppose to be reserved for the compiler |
* as explained in C standards. |
*/ |
#if HAVE_CONFIG_H |
#include <config.h> |
#endif |
|
#define _XOPEN_SOURCE /* grantpt() and al. */ |
#define _GNU_SOURCE /* on_exit */ |
|
38,7 → 42,9
#include <string.h> /* strchr() */ |
|
#ifndef __CYGWIN__ |
#if HAVE_SYS_STROPTS_H |
#include <sys/stropts.h> /* grantpt(), unlockpt() */ |
#endif |
#include <libgen.h> /* basename() */ |
#endif /* __CYGWIN__ */ |
|
109,9 → 115,12
retval->fds.fdin = -1; |
retval->fds.fdout = -1; |
retval->pid = -1; |
|
#if defined(HAS_ON_EXIT) |
/* reset cause exit(1), leaving an xterm opened */ |
on_exit(xterm_exit, retval); |
|
#endif |
|
i = 2; |
arglist = (char*)input; |
retval->argv = malloc(sizeof(char*) * MAX_XTERM_ARGS); |
136,6 → 145,7
|
static int xterm_open(void * data) |
{ |
#if defined(HAS_GRANTPT) && defined(HAS_UNLOCKPT) && defined(HAS_PTSNAME) |
struct xterm_channel * xt = data; |
int master, retval; |
char * slavename; |
166,7 → 176,8
xt->fds.fdout = xt->fds.fdin = open(slavename, O_RDWR); |
if(xt->fds.fdout < 0) goto closemastererror; |
|
#if !defined(linux) && !defined(__CYGWIN__) |
//#if !defined(linux) && !defined(__CYGWIN__) |
#if defined(I_PUSH) |
/* Linux does not support STREAMS-style line discipline, even with LiS. */ |
retval = ioctl(xt->fds.fdin, I_PUSH, "ptem"); |
if(retval < 0) goto closeslaveerror; |
226,6 → 237,14
close(master); |
xt->pid = xt->fds.fdin = xt->fds.fdout = -1; |
return -1; |
|
#else |
/* I don't see how this stuff should be working on a system that doesn't know |
grantpt(), unlockpt(), ptsname(). Mac OS X also does not have /dev/ptmx. |
-hpanther |
*/ |
return -1; |
#endif |
} |
|
struct channel_ops xterm_channel_ops = |
/trunk/or1ksim/peripheral/channels/channel.c
19,14 → 19,26
along with this program; if not, write to the Free Software |
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ |
|
#if HAVE_CONFIG_H |
#include <config.h> |
#endif |
|
#define _GNU_SOURCE /* for strndup */ |
|
#include <stdio.h> /* perror */ |
#include <stdlib.h> /* exit */ |
|
#if HAVE_MALLOC_H |
#include <malloc.h> /* calloc, free */ |
#endif |
|
#include <string.h> /* strndup, strcmp, strlen, strchr */ |
#include <errno.h> /* errno */ |
|
#ifndef HAVE_STRNDUP |
#include "extras/extras.h" |
#endif |
|
#include "channel.h" |
|
struct channel_factory |
/trunk/or1ksim/peripheral/channels/fd.c
19,12 → 19,18
along with this program; if not, write to the Free Software |
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ |
|
#if HAVE_CONFIG_H |
#include <config.h> |
#endif |
|
#include <sys/time.h> /* struct timeval */ |
#include <sys/types.h> /* fd_set */ |
#include <stdio.h> /* perror */ |
#include <stdlib.h> /* atoi */ |
#include <unistd.h> /* read, write, select */ |
#if HAVE_MALLOC_H |
#include <malloc.h> /* calloc, free */ |
#endif |
#include <string.h> /* strchr */ |
#include <errno.h> /* errno */ |
|
/trunk/or1ksim/peripheral/channels/generic.c
19,7 → 19,14
along with this program; if not, write to the Free Software |
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ |
|
#include <malloc.h> /* free */ |
#if HAVE_CONFIG_H |
#include <config.h> |
#endif |
|
#if HAVE_MALLOC_H |
#include <malloc.h> /* calloc, free */ |
#endif |
|
#include <errno.h> /* errno */ |
|
|
/trunk/or1ksim/peripheral/eth.c
52,7 → 52,7
static void eth_controller_rx_clock( struct eth_device * ); |
/* utility functions */ |
static int eth_find_controller( unsigned long addr, struct eth_device **eth, unsigned long *reladdr ); |
struct eth_device *eth_find_vapi_device (unsigned long id, unsigned *which); |
struct eth_device *eth_find_vapi_device (unsigned long id, unsigned long *which); |
static ssize_t eth_read_rx_file( struct eth_device *, void *, size_t ); |
static void eth_skip_rx_file( struct eth_device *, off_t ); |
static void eth_rewind_rx_file( struct eth_device *, off_t ); |
314,7 → 314,7
} |
|
/* Packet must be big enough to hold a header */ |
if ( eth->rx.packet_length < ETH_HLEN ){ |
if ( eth->rx.packet_length < ETHER_HDR_LEN ){ |
debug( 3, "eth_start_rx(): Packet too small\n" ); |
eth_rx_next_packet( eth ); |
|
549,7 → 549,12
if ( (eth->rxfd = open( eth->rxfile, O_RDONLY )) < 0 ) |
fprintf( stderr, "Cannot open Ethernet RX file \"%s\"\n", eth->rxfile ); |
if ( (eth->txfd = open( eth->txfile, |
O_RDWR | O_CREAT | O_APPEND | O_SYNC, |
O_RDWR | O_CREAT | O_APPEND |
|
#if defined(O_SYNC) /* BSD / Mac OS X manual doesn't know about O_SYNC */ |
| O_SYNC |
#endif |
, |
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH )) < 0 ) |
fprintf( stderr, "Cannot open Ethernet TX file \"%s\"\n", eth->txfile ); |
eth->loopback_offset = lseek( eth->txfd, 0, SEEK_END ); |
858,7 → 863,7
/* |
* Convert VAPI id to controller struct and relative address. |
*/ |
struct eth_device *eth_find_vapi_device( unsigned long id, unsigned *which ) |
struct eth_device *eth_find_vapi_device( unsigned long id, unsigned long *which ) |
{ |
unsigned i; |
|
/trunk/or1ksim/peripheral/fb.c
86,13 → 86,13
|
/* define these also for big endian */ |
#if __BIG_ENDIAN__ |
#define CNV32(x) (x) (\ |
#define CNV32(x) (\ |
((((x) >> 24) & 0xff) << 0)\ |
| ((((x) >> 16) & 0xff) << 8)\ |
| ((((x) >> 8) & 0xff) << 16)\ |
| ((((x) >> 0) & 0xff) << 24)) |
#define CNV16(x) (x) (\ |
| ((((x) >> 8) & 0xff) << 0)\ |
#define CNV16(x) (\ |
((((x) >> 8) & 0xff) << 0)\ |
| ((((x) >> 0) & 0xff) << 8)) |
#else |
#define CNV16(x) (x) |
/trunk/or1ksim/peripheral/ethernet_i.h
132,7 → 132,7
unsigned rx_channel; |
|
/* Our address */ |
unsigned char mac_address[ETH_ALEN]; |
unsigned char mac_address[ETHER_ADDR_LEN]; |
|
/* interrupt line */ |
unsigned long mac_int; |
/trunk/or1ksim/peripheral/16450.c
244,7 → 244,7
|
static void send_char (int uart, int bits_send) |
{ |
PRINTF ("'%c'\n", uarts[uart].iregs.txser); |
PRINTF ("%c", uarts[uart].iregs.txser); |
debug(4, "TX \'%c\' via UART%d...\n", uarts[uart].iregs.txser, uart); |
if (uarts[uart].regs.mcr & UART_MCR_LOOP) |
uarts[uart].iregs.loopback = uarts[uart].iregs.txser; |
424,6 → 424,7
/* Set unused character bits to zero and allow lsr register in fifo */ |
uarts[i].iregs.rxser &= ((1 << ((uarts[i].regs.lcr & 3) + 5)) - 1) | 0xff00; |
debug(4, "Receiving 0x%02x'%c' via UART%d...\n", uarts[i].iregs.rxser, uarts[i].iregs.rxser, i); |
PRINTF ("%c", uarts[i].iregs.rxser); |
uarts[i].istat.rxser_full = 0; |
uarts[i].istat.rxser_clks = 0; |
uart_add_char (i, uarts[i].iregs.rxser); |