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

Subversion Repositories ssbcc

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

Details | Compare with Previous | View Log

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

powered by: WebSVN 2.1.0

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