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

Subversion Repositories ssbcc

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

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

powered by: WebSVN 2.1.0

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