OpenCores
URL https://opencores.org/ocsvn/ssbcc/ssbcc/trunk

Subversion Repositories ssbcc

[/] [ssbcc/] [trunk/] [ssbccConfig.py] - Blame information for rev 8

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 2 sinclairrf
################################################################################
2
#
3
# Copyright 2012-2013, Sinclair R.F., Inc.
4
#
5
# Utilities required by ssbcc
6
#
7
################################################################################
8
 
9
import math
10
import os
11
import re
12
import sys
13
 
14
from ssbccUtil import *
15
 
16
class SSBCCconfig():
17
  """
18
  Container for ssbcc configuration commands, the associated parsing, and
19
  program generation.
20
  """
21
 
22
  def __init__(self):
23
    """
24
    Initialize the empty dictionaries holding the processor configuration
25
    parameters.  Initialize the paths to search for peripherals.
26
    """
27
    self.config         = dict();               # various settings, etc.
28
    self.constants      = dict();               # CONSTANTs
29
    self.functions      = dict();               # list of functions to define
30
    self.inports        = list();               # INPORT definitions
31
    self.ios            = list();               # List of I/Os
32
    self.outports       = list();               # OUTPORT definitions (see AddOutport)
33
    self.parameters     = list();               # PARAMETERs and LOCALPARAMs
34
    self.peripheral     = list();               # PERIPHERALs
35
    self.signals        = list();               # internal signals
36
    self.symbols        = list();               # constant, I/O, inport, etc.  names
37
 
38
    # list of memories
39
    self.memories = dict(name=list(), type=list(), maxLength=list());
40
 
41
    # list of how the memories will be instantiated
42
    self.config['combine'] = list();
43
 
44 3 sinclairrf
    # initial search path for .INCLUDE configuration commands
45
    self.includepaths = list();
46
    self.includepaths.append('.');
47
 
48 2 sinclairrf
    # initial search paths for peripherals
49 3 sinclairrf
    self.peripheralpaths = list();
50 2 sinclairrf
    self.peripheralpaths.append('.');
51
    self.peripheralpaths.append('peripherals');
52
    self.peripheralpaths.append(os.path.join(sys.path[0],'core/peripherals'));
53
 
54
  def AddConstant(self,name,value,loc):
55
    """
56
    Add the constant for the "CONSTANT" configuration command to the "constants"
57
    dictionary.\n
58
    name        symbol for the constant
59
    value       value of the constant
60
    loc         file name and line number for error messages
61
    """
62
    self.AddSymbol(name,loc);
63
    if name in self.constants:
64
      raise SSBCCException('CONSTANT "%s" already declared at %s' % (name,loc,));
65 8 sinclairrf
    if not IsIntExpr(value):
66
      raise SSBCCException('Could not evaluate expression "%s" for constant at %s' % (value,loc,));
67
    self.constants[name] = ParseIntExpr(value);
68 2 sinclairrf
 
69
  def AddIO(self,name,nBits,iotype,loc):
70
    """
71
    Add an I/O signal to the processor interface to the system.\n
72
    name        name of the I/O signal
73
    nBits       number of bits in the I/O signal
74
    iotype      signal direction:  "input", "output", or "inout"
75
    """
76
    if iotype != 'comment':
77
      self.AddSymbol(name,loc);
78
    self.ios.append((name,nBits,iotype,));
79
 
80
  def AddInport(self,port,loc):
81
    """
82
    Add an INPORT symbol to the processor.\n
83
    port        name of the INPORT symbol
84
    loc         file name and line number for error messages
85
    """
86
    name = port[0];
87
    self.AddSymbol(name,loc);
88
    self.inports.append(port);
89
 
90
  def AddMemory(self,cmd,loc):
91
    """
92
    Add a memory to the list of memories.\n
93
    cmd         3-element list as follows:
94
                [0] ==> type:  "RAM" or "ROM"
95
                [1] ==> memory name
96
                [2] ==> memory length (must be a power of 2)
97
    loc         file name and line number for error messages
98
    """
99
    self.memories['type'].append(cmd[0]);
100
    self.memories['name'].append(cmd[1]);
101
    maxLength = eval(cmd[2]);
102
    if not IsPowerOf2(maxLength):
103
      raise SSBCCException('Memory length must be a power of 2, not "%s", at %s' % (cmd[2],loc,));
104
    self.memories['maxLength'].append(eval(cmd[2]));
105
 
106
  def AddOutport(self,port,loc):
107
    """
108
    Add an OUTPORT symbol to the processor.\n
109
    port        tuple as follows:
110
                port[0] - name of the OUTPORT symbol
111
                port[1] - True if the outport is a strobe-only outport, false
112
                          otherwise
113
                port[2:] - zero or more tuples as follows:
114
                  (o_signal,width,type,[initialization],)
115
                where
116
                  o_signal is the name of the output signal
117
                  width is the number of bits in the signal
118
                  type is 'data' or 'strobe'
119
                  initialization is an optional initial/reset value for the
120
                    output signal
121
    loc         file name and line number for error messages
122
    """
123
    self.AddSymbol(port[0],loc);
124
    self.outports.append(port);
125
 
126
  def AddParameter(self,name,value,loc):
127
    """
128
    Add a PARAMETER to the processor.\n
129
    name        name of the PARAMETER
130
    value       value of the PARAMETER
131
    loc         file name and line number for error messages
132
    """
133
    if not re.match(r'[LG]_\w+$',name):
134
      raise Exception('Program Bug -- bad parameter name at %s' % loc);
135
    self.AddSymbol(name,loc);
136
    self.parameters.append((name,value,));
137
 
138
  def AddSignal(self,name,nBits,loc):
139
    """
140
    Add a signal without an initial value to the processor.\n
141
    name        name of the signal
142
    nBits       number of bits in the signal
143
    loc         file name and line number for error messages
144
    """
145
    self.AddSymbol(name,loc);
146
    self.signals.append((name,nBits,));
147
 
148
  def AddSignalWithInit(self,name,nBits,init,loc):
149
    """
150
    Add a signal with an initial/reset value to the processor.\n
151
    name        name of the signal
152
    nBits       number of bits in the signal
153
    init        initial/reset value of the signal
154
    loc         file name and line number for error messages
155
    """
156
    self.AddSymbol(name,loc);
157
    self.signals.append((name,nBits,init,));
158
 
159
  def AddSymbol(self,name,loc=None):
160
    """
161
    Add the specified name to the list of symbols.\n
162
    Note:  This symbol has no associated functionality and is only used for
163
           ".ifdef" conditionals.
164
    """
165
    if name in self.symbols:
166
      if loc == None:
167
        raise SSBCCException('Symbol "%s" already defined, no line number provided');
168
      else:
169
        raise SSBCCException('Symbol "%s" already defined before %s' % (name,loc,));
170
    self.symbols.append(name);
171
 
172 3 sinclairrf
  def AppendIncludePath(self,path):
173
    """
174
    Add the specified path to the end of the paths to search for .INCLUDE
175
    configuration commands.\n
176
    path        path to add to the list
177
    """
178
    self.includepaths.insert(-1,path);
179
 
180 2 sinclairrf
  def CompleteCombines(self):
181
    """
182
    Ensure all memories are assigned addresses.\n
183
    This modifies config['combine'] to include singleton entries for any
184
    memories not subject to the COMBINE configuration command.  It then computes
185
    how the memories will be packed together as well as properites for the
186
    packed memories.  These properties are:
187
      packing   how the memories will be packed as per PackCombinedMemory
188
      memName   HDL name of the memory
189
      memLength number of words in the memory
190
      memWidth  bit width of the memory words
191
    """
192
    # Create singleton entries for memory types and memories that aren't already listed in 'combine'.
193
    if not self.IsCombined('INSTRUCTION'):
194
      self.config['combine'].append({'mems':['INSTRUCTION',], 'memArch':'sync'});
195
    for memType in ('DATA_STACK','RETURN_STACK',):
196
      if not self.IsCombined(memType):
197
        self.config['combine'].append({'mems':[memType,], 'memArch':'LUT'});
198
    for memName in self.memories['name']:
199
      if not self.IsCombined(memName):
200
        self.config['combine'].append({'mems':[memName,], 'memArch':'LUT'});
201
    # Determine the HDL names for the memories.
202
    nRAMROMs = 0;
203
    for combined in self.config['combine']:
204
      if combined['mems'][0] == 'INSTRUCTION':
205
        combined['memName'] = 's_opcodeMemory';
206
      elif combined['mems'][0] == 'DATA_STACK':
207
        combined['memName'] = 's_data_stack';
208
      elif combined['mems'][0] == 'RETURN_STACK':
209
        combined['memName'] = 's_R_stack';
210
      else:
211
        nRAMROMs += 1;
212
    if nRAMROMs > 0:
213
      memNameFormat = 's_mem_%%0%dx' % ((CeilLog2(nRAMROMs)+3)/4);
214
    ixRAMROM = 0;
215
    for combined in self.config['combine']:
216
      if 'memName' in combined:
217
        continue;
218
      if nRAMROMs == 1:
219
        combined['memName'] = 's_mem';
220
      else:
221
        combined['memName'] = memNameFormat % ixRAMROM;
222
        ixRAMROM += 1;
223
    # Perform packing for all memories.
224
    for combined in self.config['combine']:
225
      self.PackCombinedMemory(combined);
226
 
227
  def Exists(self,name):
228
    """
229
    Return true if the requested attribute has been created in the ssbccConfig
230
    object.
231
    """
232
    return name in self.config;
233
 
234
  def Get(self,name):
235
    """
236
    Return the requested attribute from the ssbccConfig object.
237
    """
238
    if not self.Exists(name):
239
      raise Exception('Program Bug:  "%s" not found in config' % name);
240
    return self.config[name];
241
 
242
  def GetMemoryByBank(self,ixBank):
243
    """
244
    Return the parameters for a memory by its bank address.\n
245
    ixBank      index of the requested memory bank
246
    """
247
    if not 'bank' in self.memories:
248
      return None;
249
    if ixBank not in self.memories['bank']:
250
      return None;
251
    ixMem = self.memories['bank'].index(ixBank);
252
    return self.GetMemoryParameters(ixMem);
253
 
254
  def GetMemoryByName(self,name):
255
    """
256
    Return the parameters for a memory by its name.\n
257
    name        name of the requested memory
258
    """
259
    if not name in self.memories['name']:
260
      return None;
261
    ixMem = self.memories['name'].index(name);
262
    return self.GetMemoryParameters(ixMem);
263
 
264
  def GetMemoryParameters(self,rawIndex):
265
    """
266
    Return the parameters for a memory by its index in the list of memories.\n
267
    rawIndex    index within the list of memories
268
    """
269
    if type(rawIndex) == str:
270
      if not self.IsMemory(rawIndex):
271
        raise Exception('Program Bug:  reference to non-existent memory');
272
      ix = self.memories['name'].index(rawIndex);
273
    elif type(rawIndex) == int:
274
      if (rawIndex < 0) or (rawIndex >= len(self.memories['name'])):
275
        raise Exception('Program Bug:  bad memory index %d' % rawIndex);
276
      ix = rawIndex;
277
    else:
278
      raise Exception('Program Bug:  unrecognized index type "%s"' % type(rawIndex));
279
    outvalue = dict();
280
    outvalue['index'] = ix;
281
    for field in self.memories:
282
      outvalue[field] = self.memories[field][ix];
283
    return outvalue;
284
 
285
  def GetPacking(self,name):
286
    """
287
    Get the memory packing for the provided memory.
288
    """
289
    for combined in self.config['combine']:
290
      if name not in combined['mems']:
291
        continue;
292
      for port in combined['port']:
293
        for packing in port['packing']:
294
          if packing['name'] == name:
295
            return (combined,port,packing,);
296
    else:
297
      raise Exception('Program Bug -- %s not found in combined memories' % name);
298
 
299
  def GetParameterValue(self,name):
300
    """
301
    Get the value associated with the named parameter.
302
    """
303
    if name.find('[') != -1:
304
      ix = name.index('[');
305
      thisSlice = name[ix:];
306
      name = name[:ix];
307
    else:
308
      thisSlice = '[0+:8]';
309
    for ix in range(len(self.parameters)):
310
      if self.parameters[ix][0] == name:
311 3 sinclairrf
        return ExtractBits(IntValue(self.parameters[ix][1]),thisSlice);
312 2 sinclairrf
    else:
313
      raise Exception('Program Bug:  Parameter "%s" not found' % name);
314
 
315
  def InsertPeripheralPath(self,path):
316
    """
317
    Add the specified path to the beginning of the paths to search for
318
    peripherals.\n
319
    path        path to add to the list
320
    """
321
    self.peripheralpaths.insert(-1,path);
322
 
323
  def IsCombined(self,name):
324
    """
325
    Indicate whether or not the specified memory type has already been listed
326
    in a "COMBINE" configuration command.  The memory type should be one of
327
    DATA_STACK, INSTRUCTION, or RETURN_STACK.\n
328
    name        name of the specified memory type\n
329
    """
330
    for combined in self.config['combine']:
331
      if name in combined['mems']:
332
        return True;
333
    else:
334
      return False;
335
 
336 4 sinclairrf
  def IsConstant(self,name):
337
    """
338
    Indicate whether or not the specified symbol is a recognized constant.
339
    """
340
    if re.match(r'C_\w+$',name) and name in self.constants:
341
      return True;
342
    else:
343
      return False;
344
 
345 2 sinclairrf
  def IsMemory(self,name):
346
    """
347
    Indicate whether or not the specified symbol is the name of a memory.
348
    """
349
    return (name in self.memories['name']);
350
 
351
  def IsParameter(self,name):
352
    """
353
    Indicate whether or not the specified symbol is the name of a parameter.
354
    """
355 4 sinclairrf
    if re.match(r'[GL]_\w+$',name) and name in self.symbols:
356 2 sinclairrf
      return True;
357
    else:
358
      return False;
359
 
360
  def IsRAM(self,name):
361
    """
362
    Indicate whether or not the specified symbol is the name of a RAM.
363
    """
364
    if name not in self.memories['name']:
365
      return False;
366
    ix = self.memories['name'].index(name);
367
    return self.memories['type'][ix] == 'RAM';
368
 
369
  def IsROM(self,name):
370
    """
371
    Indicate whether or not the specified symbol is the name of a RAM.
372
    """
373
    if name not in self.memories['name']:
374
      return False;
375
    ix = self.memories['name'].index(name);
376
    return self.memories['type'][ix] == 'ROM';
377
 
378
  def IsStrobeOnlyOutport(self,outport):
379
    """
380
    Indicate whether or not the specified outport symbol only has strobes
381
    associated with it (i.e., it has no data signals).
382
    """
383
    return outport[1];
384
 
385
  def IsSymbol(self,name):
386
    """
387
    Indicate whether or not the specified name is a symbol.
388
    """
389
    return (name in self.symbols);
390
 
391
  def MemoryNameLengthList(self):
392
    """
393
    Return a list of tuples where each tuple is the name of a memory and its
394
    length.
395
    """
396
    outlist = list();
397
    for ix in range(len(self.memories['name'])):
398
      outlist.append((self.memories['name'][ix],self.memories['maxLength'][ix],));
399
    return outlist;
400
 
401
  def NInports(self):
402
    """
403
    Return the number of INPORTS.
404
    """
405
    return len(self.inports);
406
 
407
  def NMemories(self):
408
    """
409
    Return the number of memories.
410
    """
411
    return len(self.memories['name']);
412
 
413
  def NOutports(self):
414
    """
415
    Return the number of OUTPORTS.
416
    """
417
    return len(self.outports);
418
 
419
  def OverrideParameter(self,name,value):
420
    """
421
    Change the value of the specified parameter (based on the command line
422
    argument instead of the architecture file).\n
423
    name        name of the parameter to change
424
    value       new value of the parameter
425
    """
426
    for ix in range(len(self.parameters)):
427
      if self.parameters[ix][0] == name:
428
        break;
429
    else:
430
      raise SSBCCException('Command-line parameter or localparam "%s" not specified in the architecture file' % name);
431
    self.parameters[ix] = (name,value,);
432
 
433
  def PackCombinedMemory(self,combined):
434
    """
435
    Utility function for CompleteCombines.\n
436
    Determine packing strategy and resulting memory addresses and sizes.  This
437
    list has everything ssbccGenVerilog needs to construct the memory.\n
438
    The dual port memories can be used to do the following:
439
      1.  pack a single memory, either single-port or dual-port
440
      2.  pack two single-port memories sequentially, i.e., one at the start of
441
          the RAM and one toward the end of the RAM
442
      3.  pack one single-port memory at the start of the RAM and pack several
443
          compatible single-port memories in parallel toward the end of the RAM.
444
          Note:  Compatible means that they have the same address.
445
      4.  pack several compatible dual-port memories in parallel.\n
446
    These single-port or dual-port single or parallel packed memories are
447
    described in the 'port' list in combined.  Each entry in the port list has
448
    several parameters described below and a 'packing' list that describes the
449
    single or multiple memories attached to that port.\n
450
    The parameters for each of port is as follows:
451
      offset    start address of the memory in the packing
452
      nWords    number of RAM words reserved for the memory
453
                Note:  This can be larger than the aggregate number of words
454
                       required by the memory in order to align the memories to
455
                       power-of-2 address alignments.
456
      ratio     number of base memory entries for the memory
457
                Note:  This must be a power of 2.\n
458
    The contents of each entry in the packing are as follows:
459
      -- the following are from the memory declaration
460
      name      memory name
461
      length    number of elements in the memory based on the declared memory
462
                size
463
                Note:  This is based on the number of addresses required for
464
                       each memory entry (see ratio).
465
      nbits     width of the memory type
466
      -- the following are derived for the packing
467
      lane      start bit
468
                Note:  This is required in particular when memories are stacked
469
                       in parallel.
470
      nWords    number of memory addresses allocated for the memory based on
471
                the packing
472
                Note:  This will be larger than length when a small memory is
473
                       packed in parallel with a larger memory.  I.e., when
474
                       ratio is not one.
475
      ratio     number of base memory entries required to extract a single word
476
                for the memory type
477
                Note:  This allows return stack entries to occupy more than one
478
                       memory address when the return stack is combined with
479
                       other memory addresses.
480
                Note:  This must be a power of 2.\n
481
    The following entries are also added to "combined":
482
      nWords    number of words in the memory
483
      memWidth  bit width of the memory words\n
484
    Note:  If memories are being combined with the instructions space, they are
485
           always packed at the end of the instruction space, so the
486
           instruction space allocation is not included in the packing.
487
    """
488
    # Count how many memories of each type are being combined.
489
    nSinglePort = 0;
490
    nRAMs = 0;
491
    nROMs = 0;
492
    for memName in combined['mems']:
493
      if memName in ('INSTRUCTION','DATA_STACK','RETURN_STACK',):
494
        nSinglePort += 1;
495
      elif self.IsROM(memName):
496
        nROMs += 1;
497
      else:
498
        nRAMs += 1;
499
    if nRAMs > 0:
500
      nRAMs += nROMs;
501
      nROMs = 0;
502
    # Ensure the COMBINE configuration command is implementable in a dual-port RAM.
503
    if nSinglePort > 0 and nRAMs > 0:
504
      raise SSBCCException('Cannot combine RAMs with other memory types in COMBINE configuration command at %s' % combined['loc']);
505
    if nSinglePort > 2 or (nSinglePort > 1 and nROMs > 0):
506
      raise SSBCCException('Too many memory types in COMBINE configuration command at %s' % combined['loc']);
507
    # Start splitting the listed memories into the one or two output lists and ensure that single-port memories are listed in the correct order.
508
    mems = combined['mems'];
509
    ixMem = 0;
510
    split = list();
511
    if 'INSTRUCTION' in mems:
512
      if mems[0] != 'INSTRUCTION':
513
        raise SSBCCException('INSTRUCTION must be the first memory listed in the COMBINE configuration command at %s' % combined['loc']);
514
      split.append(['INSTRUCTION']);
515
      ixMem += 1;
516
    while len(mems[ixMem:]) > 0 and mems[ixMem] in ('DATA_STACK','RETURN_STACK',):
517
      split.append([mems[ixMem]]);
518
      ixMem += 1;
519
    for memName in ('DATA_STACK','RETURN_STACK',):
520
      if memName in mems[ixMem:]:
521
        raise SSBCCException('Single-port memory %s must be listed before ROMs in COMBINE configuration command at %s' % combined['loc']);
522
    if mems[ixMem:]:
523
      split.append(mems[ixMem:]);
524
    if not (1 <= len(split) <= 2):
525
      raise Exception('Program Bug -- bad COMBINE configuration command not caught');
526
    # Create the detailed packing information.
527
    combined['port'] = list();
528
    for thisSplit in split:
529
      packing = list();
530
      for memName in thisSplit:
531
        if memName == 'INSTRUCTION':
532
          packing.append({'name':memName, 'length':self.Get('nInstructions')['length'], 'nbits':9});
533
        elif memName == 'DATA_STACK':
534
          packing.append({'name':memName, 'length':self.Get('data_stack'), 'nbits':self.Get('data_width')});
535
        elif memName == 'RETURN_STACK':
536
          nbits = max(self.Get('data_width'),self.Get('nInstructions')['nbits']);
537
          packing.append({'name':memName, 'length':self.Get('return_stack'), 'nbits':nbits});
538
        else:
539
          thisMemory = self.GetMemoryParameters(memName);
540
          packing.append({'name':memName, 'length':CeilPow2(thisMemory['maxLength']), 'nbits':self.Get('data_width')});
541
      combined['port'].append({ 'packing':packing });
542
    # Calculate the width of the base memory.
543
    # Note:  This accommodates RETURN_STACK being an isolated memory.
544
    memWidth = combined['port'][0]['packing'][0]['nbits'] if len(combined['port']) == 1 else None;
545
    for port in combined['port']:
546
      for packing in port['packing']:
547
        tempMemWidth = packing['nbits'];
548
        if tempMemWidth > self.Get('sram_width'):
549
          tempMemWidth = self.Get('sram_width');
550
        if not memWidth:
551
          memWidth = tempMemWidth;
552
        elif tempMemWidth > memWidth:
553
          memWidth = tempMemWidth;
554
    combined['memWidth'] = memWidth;
555
    # Determine how the memories are packed.
556
    # Note:  "ratio" should be non-unity only for RETURN_STACK.
557
    for port in combined['port']:
558
      lane = 0;
559
      for packing in port['packing']:
560
        packing['lane'] = lane;
561
        ratio = CeilPow2((packing['nbits']+memWidth-1)/memWidth);
562
        packing['ratio'] = ratio;
563
        packing['nWords'] = ratio * packing['length'];
564
        lane += ratio;
565
    # Aggregate parameters each memory port.
566
    for port in combined['port']:
567
      ratio = CeilPow2(sum(packing['ratio'] for packing in port['packing']));
568
      maxLength = max(packing['length'] for packing in port['packing']);
569
      port['ratio'] = ratio;
570
      port['nWords'] = ratio * maxLength;
571
    combined['port'][0]['offset'] = 0;
572
    if len(combined['port']) > 1:
573
      if combined['mems'][0] == 'INSTRUCTION':
574
        nWordsTail = combined['port'][1]['nWords'];
575
        port0 = combined['port'][0];
576
        if port0['nWords'] <= nWordsTail:
577
          raise SSBCCException('INSTRUCTION length too small for "COMBINE INSTRUCTION,..." at %s' % combined['loc']);
578
        port0['nWords'] -= nWordsTail;
579
        port0['packing'][0]['nWords'] -= nWordsTail;
580
        port0['packing'][0]['length'] -= nWordsTail;
581
      else:
582
        maxNWords = max(port['nWords'] for port in combined['port']);
583
        for port in combined['port']:
584
          port['nWords'] = maxNWords;
585
      combined['port'][1]['offset'] = combined['port'][0]['nWords'];
586
    combined['nWords'] = sum(port['nWords'] for port in combined['port']);
587
 
588
  def ProcessCombine(self,loc,line):
589
    """
590
    Parse the "COMBINE" configuration command as follows:\n
591
    Validate the arguments to the "COMBINE" configuration command and append
592
    the list of combined memories and the associated arguments to "combine"
593
    property.\n
594
    The argument consists of one of the following:
595
      INSTRUCTION,{DATA_STACK,RETURN_STACK,rom_list}
596
      DATA_STACK
597
      DATA_STACK,{RETURN_STACK,rom_list}
598
      RETURN_STACK
599
      RETURN_STACK,{DATA_STACK,rom_list}
600
      mem_list
601
    where rom_list is a comma separated list of one or more ROMs and mem_list is
602
    a list of one or more RAMs or ROMs.
603
    """
604
    # Perform some syntax checking and get the list of memories to combine.
605
    cmd = re.findall(r'\s*COMBINE\s+(\S+)\s*$',line);
606
    if not cmd:
607
      raise SSBCCException('Malformed COMBINE configuration command on %s' % loc);
608
    mems = re.split(r',',cmd[0]);
609
    if (len(mems)==1) and ('INSTRUCTION' in mems):
610
      raise SSBCCException('"COMBINE INSTRUCTION" doesn\'t make sense at %s' % loc);
611
    if ('INSTRUCTION' in mems) and (mems[0] != 'INSTRUCTION'):
612
      raise SSBCCException('"INSTRUCTION" must be listed first in COMBINE configuration command at %s' % loc);
613
    recognized = ['INSTRUCTION','DATA_STACK','RETURN_STACK'] + self.memories['name'];
614
    unrecognized = [memName for memName in mems if memName not in recognized];
615
    if unrecognized:
616
      raise SSBCCException('"%s" not recognized in COMBINE configuration command at %s' % (unrecognized[0],loc,));
617
    alreadyUsed = [memName for memName in mems if self.IsCombined(memName)];
618
    if alreadyUsed:
619
      raise SSBCCException('"%s" already used in COMBINE configuration command before %s' % (alreadyUsed[0],loc,));
620
    repeated = [mems[ix] for ix in range(len(mems)-1) if mems[ix] in mems[ix+1]];
621
    if repeated:
622
      raise SSBCCException('"%s" repeated in COMBINE configuration command on %s' % (repeated[0],loc,));
623
    # Count the number of the different memory types being combined and validate the combination.
624
    nSinglePort = sum([thisMemName in ('INSTRUCTION','DATA_STACK','RETURN_STACK',) for thisMemName in mems]);
625
    nROM = len([thisMemName for thisMemName in mems if self.IsROM(thisMemName)]);
626
    nRAM = len([thisMemName for thisMemName in mems if self.IsRAM(thisMemName)]);
627
    if nRAM > 0:
628
      nRAM += nROM;
629
      nROM = 0;
630
    if nROM > 0:
631
      nSinglePort += 1;
632
    nDualPort = 1 if nRAM > 0 else 0;
633
    if nSinglePort + 2*nDualPort > 2:
634
      raise SSBCCException('Too many ports required for COMBINE configuration command at %s' % loc);
635
    # Append the listed memory types to the list of combined memories.
636
    self.config['combine'].append({'mems':mems, 'memArch':'sync', 'loc':loc});
637
 
638
  def ProcessInport(self,loc,line):
639
    """
640
    Parse the "INPORT" configuration commands as follows:
641
      The configuration command is well formatted.
642
      The number of signals matches the corresponding list of signal declarations.
643
      The port name starts with 'I_'.
644
      The signal declarations are valid.
645
        n-bit where n is an integer
646
        set-reset
647
        strobe
648
      That no other signals are specified in conjunction with a "set-reset" signal.
649
      The total input data with does not exceed the maximum data width.\n
650
    The input port is appended to the list of inputs as a tuple.  The first
651
    entry in the tuple is the port name.  The subsequent entries are tuples
652
    consisting of the following:
653
      signal name
654
      signal width
655
      signal type
656
    """
657
    cmd = re.findall(r'\s*INPORT\s+(\S+)\s+(\S+)\s+(I_\w+)\s*$',line);
658
    if not cmd:
659
      raise SSBCCException('Malformed INPORT statement at %s: "%s"' % (loc,line[:-1],));
660
    modes = re.findall(r'([^,]+)',cmd[0][0]);
661
    names = re.findall(r'([^,]+)',cmd[0][1]);
662
    portName = cmd[0][2];
663
    if len(modes) != len(names):
664
      raise SSBCCException('Malformed INPORT configuration command -- number of options don\'t match on %s: "%s"' % (loc,line[:-1],));
665
    # Append the input signal names, mode, and bit-width to the list of I/Os.
666
    has__set_reset = False;
667
    nBits = 0;
668
    thisPort = (portName,);
669
    for ix in range(len(names)):
670
      if re.match(r'^\d+-bit$',modes[ix]):
671
        thisNBits = int(modes[ix][0:-4]);
672
        self.AddIO(names[ix],thisNBits,'input',loc);
673
        thisPort += ((names[ix],thisNBits,'data',),);
674
        nBits = nBits + thisNBits;
675
      elif modes[ix] == 'set-reset':
676
        has__set_reset = True;
677
        self.AddIO(names[ix],1,'input',loc);
678
        thisPort += ((names[ix],1,'set-reset',),);
679
        self.AddSignal('s_SETRESET_%s' % names[ix],1,loc);
680
      elif modes[ix] == 'strobe':
681
        self.AddIO(names[ix],1,'output',loc);
682
        thisPort += ((names[ix],1,'strobe',),);
683
      else:
684
        raise SSBCCException('Unrecognized INPORT signal type "%s"' % modes[ix]);
685
      if has__set_reset and len(names) > 1:
686
        raise SSBCCException('set-reset cannot be simultaneous with other signals in "%s"' % line[:-1]);
687
      if nBits > self.Get('data_width'):
688
        raise SSBCCException('Signal width too wide in "%s"' % line[:-1]);
689
    self.AddInport(thisPort,loc);
690
 
691
  def ProcessOutport(self,line,loc):
692
    """
693
    Parse the "OUTPORT" configuration commands as follows:
694
      The configuration command is well formatted.
695
      The number of signals matches the corresponding list of signal declarations.
696
      The port name starts with 'O_'.
697
      The signal declarations are valid.
698
        n-bit[=value]
699
        strobe
700
      The total output data with does not exceed the maximum data width.\n
701
    The output port is appended to the list of outports as a tuple.  The first
702
    entry in this tuple is the port name.  The subsequent entries are tuples
703
    consisting of the following:
704
      signal name
705
      signal width
706
      signal type
707
      initial value (optional)
708
    """
709
    cmd = re.findall(r'^\s*OUTPORT\s+(\S+)\s+(\S+)\s+(O_\w+)\s*$',line);
710
    if not cmd:
711
      raise SSBCCException('Malformed OUTPUT configuration command on %s: "%s"' % (loc,line[:-1],));
712
    modes = re.findall(r'([^,]+)',cmd[0][0]);
713
    names = re.findall(r'([^,]+)',cmd[0][1]);
714
    portName = cmd[0][2];
715
    if len(modes) != len(names):
716
      raise SSBCCException('Malformed OUTPORT configuration command -- number of widths/types and signal names don\'t match on %s: "%s"' % (loc,line[:-1],));
717
    # Append the input signal names, mode, and bit-width to the list of I/Os.
718
    nBits = 0;
719
    isStrobeOnly = True;
720
    thisPort = tuple();
721
    for ix in range(len(names)):
722
      if re.match(r'\d+-bit',modes[ix]):
723
        isStrobeOnly = False;
724
        a = re.match(r'(\d+)-bit(=\S+)?$',modes[ix]);
725
        if not a:
726
          raise SSBCCException('Malformed bitwith/bitwidth=initialization on %s:  "%s"' % (loc,modes[ix],));
727
        thisNBits = int(a.group(1));
728
        self.AddIO(names[ix],thisNBits,'output',loc);
729
        if a.group(2):
730
          thisPort += ((names[ix],thisNBits,'data',a.group(2)[1:],),);
731
        else:
732
          thisPort += ((names[ix],thisNBits,'data',),);
733
        nBits = nBits + thisNBits;
734
        self.config['haveBitOutportSignals'] = 'True';
735
      elif modes[ix] == 'strobe':
736
        self.AddIO(names[ix],1,'output',loc);
737
        thisPort += ((names[ix],1,'strobe',),);
738
      else:
739
        raise SSBCCException('Unrecognized OUTPORT signal type on %s: "%s"' % (loc,modes[ix],));
740
      if nBits > 8:
741
        raise SSBCCException('Signal width too wide on %s:  in "%s"' % (loc,line[:-1],));
742
    self.AddOutport((portName,isStrobeOnly,)+thisPort,loc);
743
 
744
  def ProcessPeripheral(self,loc,line):
745
    """
746
    Process the "PERIPHERAL" configuration command as follows:
747
      Validate the format of the configuration command.
748
      Find the peripheral in the candidate list of paths for peripherals.
749
      Execute the file declaring the peripheral.
750
        Note:  This is done since I couldn't find a way to "import" the
751
               peripheral.  Executing the peripheral makes its definition local
752
               to this invokation of the ProcessPeripheral function, but the
753
               object subsequently created retains the required functionality
754
               to instantiate the peripheral
755
      Go through the parameters for the peripheral and do the following for each:
756
        If the argument for the peripheral is the string "help", then print the
757
          docstring for the peripheral and exit.
758
        Append the parameter name and its argument to the list of parameters
759
          (use "None" as the argument if no argument was provided).
760
      Append the instantiated peripheral to the list of peripherals.
761
        Note:  The "exec" function dynamically executes the instruction to
762
               instantiate the peripheral and append it to the list of
763
               peripherals.
764
    """
765
    # Validate the format of the peripheral configuration command and the the name of the peripheral.
766
    cmd = re.findall(r'\s*PERIPHERAL\s+(\w+)\s*(.*)$',line);
767
    if not cmd:
768
      raise SSBCCException('Missing peripheral name in %s:  %s' % (loc,line[:-1],));
769
    peripheral = cmd[0][0];
770
    # Find and execute the peripheral Python script.
771
    # Note:  Because "execfile" and "exec" method are used to load the
772
    #        peripheral python script, the __file__ object is set to be this
773
    #        file, not the peripheral source file.
774
    for testPath in self.peripheralpaths:
775
      fullperipheral = os.path.join(testPath,'%s.py' % peripheral);
776
      if os.path.isfile(fullperipheral):
777
        break;
778
    else:
779
      raise SSBCCException('Peripheral "%s" not found' % peripheral);
780
    execfile(fullperipheral);
781
    # Convert the space delimited parameters to a list of tuples.
782
    param_list = list();
783
    for param_string in re.findall(r'(\w+="[^"]*"|\w+=\S+|\w+)\s*',cmd[0][1]):
784
      if param_string == "help":
785
        exec('helpmsg = %s.__doc__' % peripheral);
786
        if not helpmsg:
787
          raise SSBCCException('No help for peripheral %s is provided' % fullperipheral);
788
        print;
789
        print 'Help message for peripheral:  %s' % peripheral;
790
        print 'Located at:  %s' % fullperipheral;
791
        print;
792
        print helpmsg;
793
        raise SSBCCException('Terminated by "help" for peripheral %s' % peripheral);
794
      ix = param_string.find('=');
795
      if param_string.find('="') > 0:
796
        param_list.append((param_string[:ix],param_string[ix+2:-1],));
797
      elif param_string.find('=') > 0:
798
        param_list.append((param_string[:ix],param_string[ix+1:],));
799
      else:
800
        param_list.append((param_string,None));
801
    # Add the peripheral to the micro controller configuration.
802
    exec('self.peripheral.append(%s(fullperipheral,self,param_list,loc));' % peripheral);
803
 
804
  def Set(self,name,value):
805
    """
806
    Create or override the specified attribute in the ssbccConfig object.
807
    """
808
    self.config[name] = value;
809
 
810
  def SetMemoryBlock(self,name,value,errorInfo):
811
    """
812
    Set an attribute in the ssbccConfig object for the specified memory with
813
    the specified memory architecture.\n
814
    "value" must be a string with the format "\d+" or "\d+*\d+" where "\d+" is
815
    an integer.  The first format specifies a single memory with the stated
816
    size and the size must be a power of two.  The second format specified
817
    allocation of multiple memory blocks where the size is given by the first
818
    integer and must be a power of 2 and the number of blocks is given by the
819
    second integer and doesn't need to be a power of 2.
820
    """
821
    findStar = value.find('*');
822
    if findStar == -1:
823
      blockSize = int(value);
824
      nBlocks = 1;
825
    else:
826
      blockSize = int(value[0:findStar]);
827
      nBlocks = int(value[findStar+1:]);
828
    nbits_blockSize = int(round(math.log(blockSize,2)));
829
    if blockSize != 2**nbits_blockSize:
830 3 sinclairrf
      raise SSBCCException('block size must be a power of 2 at %s: "%s"' % errorInfo);
831 2 sinclairrf
    nbits_nBlocks = CeilLog2(nBlocks);
832
    self.Set(name, dict(
833
                   length=blockSize*nBlocks,
834
                   nbits=nbits_blockSize+nbits_nBlocks,
835
                   blockSize=blockSize,
836
                   nbits_blockSize=nbits_blockSize,
837
                   nBlocks=nBlocks,
838
                   nbits_nBlocks=nbits_nBlocks));
839
 
840
  def SetMemoryParameters(self,memParam,values):
841
    """
842
    Record the body of the specified memory based on the assembler output.
843
    """
844
    index = memParam['index'];
845
    for field in values:
846
      if field not in self.memories:
847
        self.memories[field] = list();
848
        for ix in range(len(self.memories['name'])):
849
          self.memories[field].append(None);
850
      self.memories[field][index] = values[field];
851
 
852
  def SignalLengthList(self):
853
    """
854
    Generate a list of the I/O signals and their lengths.
855
    """
856
    outlist = list();
857
    for io in self.ios:
858
      if io[2] == 'comment':
859
        continue;
860
      outlist.append((io[0],io[1],));
861
    return outlist;

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.