1;;; gEDA - GPL Electronic Design Automation
2;;; gnetlist - gEDA Netlist
3;;; Copyright (C) 1998-2010 Ales Hvezda
4;;; Copyright (C) 1998-2010 gEDA Contributors (see ChangeLog for details)
5;;;
6;;; This program is free software; you can redistribute it and/or modify
7;;; it under the terms of the GNU General Public License as published by
8;;; the Free Software Foundation; either version 2 of the License, or
9;;; (at your option) any later version.
10;;;
11;;; This program is distributed in the hope that it will be useful,
12;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
13;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14;;; GNU General Public License for more details.
15;;;
16;;; You should have received a copy of the GNU General Public License
17;;; along with this program; if not, write to the Free Software
18;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19;;; MA 02111-1301 USA.
20
21;;
22;; SystemC netlist backend written by Jaume Masip
23;; (based on gnet-verilog.scm by Mike Jarabek)
24
25;; some useful regexes for working with net-names
26;;
27(use-modules (ice-9 regex))
28
29(define id-regexp "[a-zA-Z_][a-zA-Z0-9_$]*")
30(define numeric  "[0-9]+")
31;; match on a systemc identifier like:  netname[x:y]
32(define bit-range-reg (make-regexp
33		       (string-append "^(" id-regexp ")[[:space:]]*"
34				      "\\["
35				      "[[:space:]]*(" numeric ")[[:space:]]*"
36				      ":"
37				      "[[:space:]]*(" numeric ")[[:space:]]*"
38				      "\\]")))
39
40;; match on a systemc identifier like:  netname[x]
41(define single-bit-reg (make-regexp
42			(string-append "^(" id-regexp ")[[:space:]]*"
43				       "\\["
44				       "[[:space:]]*(" numeric ")[[:space:]]*"
45				       "\\]" )))
46
47;; match on a systemc identifier like:  netname<type>
48(define systemc-reg (make-regexp
49                        (string-append "^(" id-regexp ")[[:space:]]*"
50                                       "<"
51                                       "[[:space:]]*(" id-regexp ")[[:space:]]*"
52                                       ">" )))
53
54;; match on a systemc identifier like:  netname
55(define simple-id-reg (make-regexp
56		       ( string-append "^(" id-regexp ")$" )))
57
58
59;; return the top level block name for the module
60(define systemc:get-module-name
61  ( gnetlist:get-toplevel-attribute "module_name" ))
62
63;; return a list of nets whose pins have the desired attribute name/value
64;; pair
65(define systemc:get-matching-nets
66  (lambda (attribute value)
67    (map car (systemc:filter attribute value packages))))
68
69;; This function takes an attribute name, desired value, and a list of
70;; packages.  For each of the packages, it looks up that attribute, and
71;; if it matches, that package name is added to the list, and the function
72;; recurses on the remaining packages.  If the attribute does not match,
73;; the function just recuses on the remaing packages. Thanks to Mohina Lal
74;; for this trick.
75;;
76
77(define systemc:filter
78  (lambda (attribute value package-list)
79    (cond ((null? package-list) '())
80	  ((string=? (gnetlist:get-package-attribute (car package-list)
81						      attribute) value)
82	   (cons
83	    (map (lambda (pin)
84		   (car (gnetlist:get-nets (car package-list) pin)))
85		 (pins (car package-list)))
86	    (systemc:filter attribute value (cdr package-list))))
87	  (else (systemc:filter attribute value (cdr package-list)))))
88)
89
90
91;;
92;; Output the guts of the module ports here
93;;
94;; Scan through the list of components, and pins from each one, finding the
95;; pins that have PINTYPE == CHIPIN, CHIPOUT, CHIPTRI (for inout)
96;; build three lists one each for the inputs, outputs and inouts
97;; return the a list of three lists that contain the pins in the order
98;; we want.
99(define systemc:get-port-list
100  (lambda ()
101    ;; construct list
102    (list (systemc:get-matching-nets "device" "IPAD")
103	  (systemc:get-matching-nets "device" "OPAD")
104	  (systemc:get-matching-nets "device" "IOPAD"))))
105
106;;
107;; output the meat of the module port section
108;;
109;; each line in the declaration is formatted like this:
110;;
111;;       PORTNAME , <newline>
112;;
113(define systemc:write-module-declaration
114  (lambda (module-name port-list p)
115    (begin
116
117      (display "#include \"systemc.h\"\n" p)
118
119      (for-each (lambda (package)         ; loop on packages
120                  (begin
121                    (let ((device (get-device package)))
122                      (if (not (memv (string->symbol device) ; ignore specials
123                                     (map string->symbol (list "IOPAD" "IPAD" "OPAD" "HIGH" "LOW"))))
124                          (begin
125                            (display "#include \"" p)
126                            (systemc:display-escaped-identifier (get-device package) p)
127                            (display ".h\"\n" p))))))
128                packages)
129      (newline p)
130      (display "SC_MODULE (" p) (systemc:display-escaped-identifier module-name p) (display ")\n{\n" p)
131    )
132  )
133)
134;;
135;; output the module direction section
136;;
137(define systemc:write-port-directions
138  (lambda (port-list p)
139    (let ((in    (car   port-list))    ; extract list of pins
140	  (out   (cadr  port-list))
141	  (inout (caddr port-list)))
142      (begin
143	(display "/* Port directions begin here */" p)
144	(newline p)
145	(for-each (lambda (pin)
146		    (begin
147      (display "sc_in<bool> " p)(systemc:display-escaped-identifier (systemc:netname pin) p)(display ";" p)(newline p)
148
149;; (display "sc_in<" p)(display (cadr (cadr pin)) p) (display "> " p) (systemc:display-wire pin p)(display ";" p)(newline p)
150;; (display "  /* " )(display(car pin) ) (display " */ " ) (display(cdr pin))
151;; (display "  /* " )(systemc:display-escaped-identifier (systemc:netname pin) ) (display " */ " )
152                    )) in)       ; do each input
153
154	(for-each (lambda (pin)
155		    (begin
156		      (display "sc_out<bool> " p)
157		      (systemc:display-escaped-identifier
158		       (systemc:netname pin) p)
159		      (display ";" p)
160		      (newline p))) out)      ; do each output
161
162	(for-each (lambda (pin)
163		    (begin
164		      (display "sc_inout<bool> " p)
165		      (systemc:display-escaped-identifier
166		       (systemc:netname pin) p)
167		      (display ";" p)
168		      (newline p))) inout)    ; do each inout
169
170	))))
171;;
172;; Top level header
173;;
174
175(define systemc:write-top-header
176	(lambda (p)
177	  (let ((port-list (systemc:get-port-list)))
178	    (begin
179	      (display "/* structural SystemC generated by gnetlist */\n" p)
180	      (display "/* WARNING: This is a generated file, edits */\n" p)
181	      (display "/*        made here will be lost next time  */\n" p)
182	      (display "/*        you run gnetlist!                 */\n" p)
183	      (display "/* Id ........gnet-systemc.scm (04/09/2003) */\n" p)
184	      (display "/* Source...../home/geda/gnet-systemc.scm   */\n" p)
185	      (display "/* Revision...0.3 (23/09/2003)              */\n" p)
186	      (display "/* Author.....Jaume Masip                   */\n" p)
187	      (newline p)
188	      (systemc:write-module-declaration systemc:get-module-name
189						port-list p)
190	      (newline p)
191	      (systemc:write-port-directions port-list p)
192	      (newline p)))))
193
194;;
195;; Footer for file
196;;
197(define systemc:write-bottom-footer
198  (lambda (p)
199    (display "  }\n};\n" p)
200    (newline p)
201    )
202)
203
204;;
205;; Take a netname and parse it into a structure that describes the net:
206;;
207;;    (   netname            ; name of the wire
208;;      ( N1                 ; first limit
209;;        N2                 ; second limit
210;;        Increasing_order   ; #t if N2>N1
211;;        sure               ; #t if we are sure about the order
212;;      ))
213(define systemc:net-parse
214  (lambda (netname)
215    (let
216	((bit-range (regexp-exec bit-range-reg netname))
217	 (single-bit (regexp-exec single-bit-reg netname))
218	 (simple-id (regexp-exec simple-id-reg netname))
219	 (systemc   (regexp-exec systemc-reg netname)))
220
221;;      (newline)
222;;      (display "    systemc:net-parse ")
223;;      (if systemc (begin (display systemc) (display "->") (display (match:substring systemc 2) )))
224;;      (if simple-id (display simple-id))
225;;      (newline)
226
227      ;; check over each expression type, and build the appropriate
228      ;; result
229      ;(display netname) (display ": ")
230      (cond
231       ;; is it a bit range?
232       (bit-range
233	;(display "bit-range" )
234	(list (match:substring bit-range 1)
235	      (list (string->number (match:substring bit-range 2))
236		    (string->number (match:substring bit-range 3))
237		    (> (string->number (match:substring bit-range 3))
238		       (string->number (match:substring bit-range 2)))
239		    '#t netname)))
240
241       ;; just a single bit?
242       (single-bit
243	;(display "single-bit")
244	(list (match:substring single-bit 1)
245	      (list (string->number (match:substring single-bit 2))
246		    (string->number (match:substring single-bit 2))
247		    '#f '#f netname)))
248
249       ;; just a systemc signal?
250       (systemc
251         (begin
252;;            (display "done systemc")(newline)
253           (list (match:substring systemc 1)
254             (list (string->number (match:substring systemc 2))
255               (match:substring systemc 2)
256;;                (string->number (match:substring systemc 2))
257                    '#f '#f netname)))
258)
259
260       ;; or a net without anything
261       (simple-id
262	;(display "bare-net")
263	(list (match:substring simple-id 1) (list 0 0 #f #f netname)))
264
265       (else
266	(display
267	 (string-append "Warning: `" netname
268			"' is not likely a valid Verilog identifier"))
269	(newline)
270	(list netname (list 0 0 #f #f netname)))
271       )))
272)
273
274;;
275;; Return #t if the passed name is something that might pass as a
276;; systemc identifier.
277;;
278(define systemc:identifier?
279  (lambda (netname)
280    (let
281	((bit-range (regexp-exec bit-range-reg netname))
282	 (single-bit (regexp-exec single-bit-reg netname))
283	 (simple-id (regexp-exec simple-id-reg netname))
284	 (systemc (regexp-exec systemc-reg netname)))
285
286      ;; check over each expression type, return
287      ;; result
288      ;(display netname) (display ": ")
289      (cond
290       (bit-range  `#t )
291       (single-bit `#t )
292       (simple-id  `#t )
293       (systemc    `#t )
294       (else       `#f )
295       ))))
296
297;;
298;; Display a systemc identifier that is escaped if needed
299;;
300(define systemc:display-escaped-identifier
301  (lambda (netname port)
302    (if (systemc:identifier? netname)
303	(display netname port) ; just display the identifier
304	;;(display (string-append "\\" netname " ") port)))) ; need to escape
305	(display netname port)))) ; need to escape
306
307
308;;
309;; return just the netname part of a systemc identifier
310;;
311(define systemc:netname
312  (lambda (netname)
313    (car (systemc:net-parse netname))))
314
315;;  Update the given bit range with data passed.  Take care
316;;  of ordering issues.
317;;
318;;   n1     : new first range
319;;   n2     : new second range
320;;   old-n1 : first range to be updated
321;;   old-n2 : second range to be updated
322;;   increasing : original order was increasing
323(define systemc:update-range
324  (lambda (n1 n2 old-n1 old-n2 increasing)
325    (let ((rn1 (if increasing
326		   (min n1 old-n1)     ; originally increasing
327		   (max n1 old-n1)))   ; originally decreasing
328
329	  (rn2 (if increasing
330		   (max n2 old-n2)     ; originally increasing
331		   (min n2 old-n2))))
332;      (display (string-append "increasing:"
333;			      (if increasing "increasing" "decreasing")
334;			      " rn1:" (number->string rn1)
335;			      " rn2:" (number->string rn2)
336;			      " n1:" (number->string n1)
337;			      " n2:" (number->string n2)
338;			      " old-n1:" (number->string old-n1)
339;			      " old-n2:" (number->string old-n2))) (newline)
340      (list rn1 rn2)
341
342      )))
343
344
345;; return a record that has been updated with the given
346;; parameters
347(define systemc:update-record
348  (lambda (n1
349	   n2
350	   list-n1
351	   list-n2
352	   increasing
353	   sure
354	   real)
355    (list
356     (append (systemc:update-range
357	      n1 n2 list-n1 list-n2
358	      increasing)
359	     (list increasing
360		   sure
361		   real)))))
362
363;;
364;;  Work over the list of `unique' nets in the design,
365;;  extracting names, and bit ranges, if appropriate.
366;;  return a list of net description objects
367;;
368
369(define systemc:get-nets '())
370
371(define systemc:get-nets-once!
372  (lambda nil
373    (define the-nets '())
374    (set! systemc:get-nets
375      (begin
376        (for-each
377          (lambda (netname)
378            ; parse the netname, and see if it is already on the list
379            (let* ((parsed (systemc:net-parse netname))
380                   (listed (assoc (car parsed) the-nets)))
381
382;;             (display  "systemc:get-nets(parsed)-> ")
383;;             (display parsed)(display " (listed)-> ")
384;;             (display listed)
385;;             (newline)
386
387             (if listed
388                 (begin ; it is, do some checks, and update the record
389                   ;; extract fields from list
390                   (let* ((list-name       (car listed))
391                          (list-n1         (car (cadr listed)))
392                          (list-n2         (cadr (cadr listed)))
393                          (list-increasing (caddr (cadr listed)))
394                          (list-sure       (cadddr (cadr listed)))
395                          (list-real       (cadddr (cdr (cadr listed))))
396
397                          (name            (car parsed))
398                          (n1              (car (cadr parsed)))
399                          (n2              (cadr (cadr parsed)))
400                          (increasing      (caddr (cadr parsed)))
401                          (sure            (cadddr (cadr parsed)))
402                          (real            (cadddr (cdr (cadr parsed))))
403
404                          (consistant      (or (and list-increasing increasing)
405                                               (and (not list-increasing)
406                                                    (not increasing))))
407
408                         )
409
410                     (cond
411                      ((and list-sure consistant)
412                       (begin
413                         (set-cdr! listed
414                                   (systemc:update-record n1 n2
415                                                          list-n1 list-n2
416                                                          increasing
417                                                          #t
418                                                          real)
419                                   )))
420                       ((and list-sure (not sure) (zero? n1) (zero? n2))
421                        '() ;; this is a net without any expression, leave it
422                        )
423                      ((and list-sure (not consistant))
424                       (begin      ;; order is inconsistent
425                         (display
426                          (string-append "Warning: Net `" real "' has a "
427                                         "bit order that conflicts with "
428                                         "the original definition of `"
429                                         list-real "', ignoring `"
430                                         real "'"
431                                         ))
432                         (newline)))
433                       ((and (not list-sure) sure consistant)
434                        (begin
435                          (set-cdr! listed
436                                    (systemc:update-record n1 n2
437                                                           list-n1 list-n2
438                                                           increasing
439                                                           #t
440                                                           real))))
441
442                       ((and (not list-sure) sure (not consistant))
443                        (begin
444                          (set-cdr! listed
445                                    (systemc:update-record n1 n2
446                                                           list-n2 list-n1
447                                                           increasing
448                                                           #t
449                                                           real))))
450                       ((and (not list-sure) (not sure))
451                        (begin
452                          (set-cdr! listed
453                                    (systemc:update-record n1 n2
454                                                           list-n1 list-n2
455                                                           increasing
456                                                           #f
457                                                           real))))
458                       (else
459                        (begin
460                          (display "This should never happen!")
461                          (newline)))
462                       )
463                 )
464             )
465           (begin ; it is not, just add it to the end
466             (set! the-nets
467                   (append the-nets
468                           (list parsed))))
469           ))
470;;         (display  "systemc:get-nets(parsed)-> ")
471         )
472
473        all-unique-nets)
474      the-nets)
475    )
476    systemc:get-nets
477))
478
479;;
480;;  Display wires from the design
481;;
482;;  Display a net in a legal systemc format, based on the object passed
483(define systemc:display-wire
484  (lambda (wire p)
485    ;; figure out if we need a bit range
486    (let ((name            (car wire))
487	  (n1              (car (cadr wire)))
488	  (n2              (cadr (cadr wire)))
489          (increasing      (caddr (cadr wire)))
490	  )
491
492;;      (if (not (and (zero? n1) (zero? n2)))
493;;	  (begin     ;; yes, print it
494;;	    (display "[ " p)(display n1 p)(display " : " p)(display n2 p)(display " ] " p) ) )
495    ;; print the wire name
496      (systemc:display-escaped-identifier name p)
497      ;;(systemc:display-escaped-identifier n1 p)
498      ;;(systemc:display-escaped-identifier n2 p)
499      ;;(systemc:display-escaped-identifier increasing p)
500    )
501  )
502)
503
504;;
505;;  Loop over the list of nets in the design, writing one by one
506;;
507(define systemc:write-wires
508  (lambda (p)
509    (display "/* Wires from the design */" p)
510    (newline p)
511    (for-each (lambda (wire)          ; print a wire statement for each
512    ;;            (let ((name (car wire)) (n1 (car (cadr wire))) (n2 (cadr (cadr wire))) (increasing (caddr (cadr wire)))))
513;;    (display "/* Wires from the design */")(newline)
514;;                (display "systemc:write-wires -> ")(display wire)(newline)
515		(display "sc_signal<" p)
516                (display (cadr (cadr wire)) p)
517		(display "> " p)
518		(systemc:display-wire wire p)
519		(display ";" p)
520		(newline p))
521	      systemc:get-nets )
522    (newline p)))
523
524;;
525;;  Output any continuous assignment statements generated
526;; by placing `high' and `low' components on the board
527;;
528(define systemc:write-continuous-assigns
529  (lambda (p)
530;;    (display "/* continuous assignments */" p) (newline p)
531    (for-each (lambda (wire)             ; do high values
532		(begin
533		  (display "assign " p)
534		  ;; XXX fixme, multiple bit widths!
535		  (systemc:display-escaped-identifier wire p)
536		  (display " = 1'b1;" p)
537		  (newline p)))
538	      (systemc:get-matching-nets "device" "HIGH"))
539
540    (for-each (lambda (wire)
541		(begin
542		  (display "assign " p)
543		  ;; XXX fixme, multiple bit widths!
544		  (systemc:display-escaped-identifier wire p)
545		  (display " = 1'b0;" p)
546		  (newline p)))
547	      (systemc:get-matching-nets "device" "LOW"))
548    (newline p))
549)
550
551
552
553;;
554;; Top level component writing
555;;
556;; Output a compoment instatantiation for each of the
557;; components on the board
558;;
559;; use the format:
560;;
561;;  device-attribute refdes (
562;;        .pinname ( net_name ),
563;;        ...
564;;    );
565;;
566
567(define c_p #f)
568
569(define systemc:components
570  (lambda (packages port)
571    (begin
572      (set! c_p #f)
573      (display "/* Package instantiations */" port) (newline port)
574
575      (for-each (lambda (package)         ; loop on packages
576                  (begin
577                    (let ((device (get-device package)))
578                      (if (not (memv (string->symbol device) ; ignore specials
579                                     (map string->symbol (list "IOPAD" "IPAD" "OPAD" "HIGH" "LOW"))))
580                          (begin
581                            (systemc:display-escaped-identifier (get-device package) port) (display " " port)
582                            (systemc:display-escaped-identifier package port) (display ";" port)
583                            (newline port))))))
584                packages)
585
586      (newline port)
587      (display "SC_CTOR(" port) (systemc:display-escaped-identifier systemc:get-module-name port)
588      (display "):\n" port)
589
590      (for-each (lambda (package)         ; loop on packages
591                  (begin
592                    (let ((device (get-device package)))
593                      (if (not (memv (string->symbol device) ; ignore specials
594                                     (map string->symbol (list "IOPAD" "IPAD" "OPAD" "HIGH" "LOW"))))
595                          (begin
596                            (if c_p (begin (display "," port) (newline port)) (set! c_p #t))
597                            (display "    " port)
598                            (systemc:display-escaped-identifier package port)
599                            (display "(\"" port)
600                            (systemc:display-escaped-identifier package port)
601;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
602
603;;(do ((i n (- i 1))) ((zero? i)) (move-n-turn (/ 360 n)))
604(do ((lp 1 (+ lp 1))) ((> lp 32))
605;;  (begin (display lp)(newline)))
606  (let* ((attr (string-append "attr" (number->string lp)))
607       (description (gnetlist:get-package-attribute package attr)))
608      (begin
609          (if (not (string=? description "unknown"))
610               (begin (display "\",\"" port) (display description port)))))
611)
612
613;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
614                             (display "\")" port)
615                            )))))
616                packages)
617      (display "\n  {" port)
618
619      (for-each (lambda (package)         ; loop on packages
620		  (begin
621		    (let ((device (get-device package)))
622 		      (if (not (memv (string->symbol device) ; ignore specials
623 				     (map string->symbol (list "IOPAD" "IPAD" "OPAD" "HIGH" "LOW"))))
624 			  (begin
625			    ; if this module wants positional pins,
626			    ; then output that format, otherwise
627			    ; output normal named declaration
628			    (systemc:display-connections package
629			     (string=? (gnetlist:get-package-attribute package "VERILOG_PORTS" ) "POSITIONAL") port)
630 			    )))))
631 		packages))))
632
633;;
634;; output a module connection for the package given to us with named ports
635;;
636(define systemc:display-connections
637   (lambda (package positional port)
638     (begin
639       (let ( (pin-list (gnetlist:get-pins-nets package))
640	      (comma_pending #f) )
641 	(if (not (null? pin-list))
642 	    (begin
643	      (newline port)
644 	      (for-each (lambda (pin)
645 			  (if (not  (strncmp? (cdr pin) "unconnected_pin" 15) )
646			      (begin
647 			        (display "    " port)(systemc:display-escaped-identifier package port)
648				(systemc:display-pin pin positional port)
649                                (display ";" port) (newline port))))
650 			pin-list)
651 	      )))))
652)
653
654;;
655;; Display the individual net connections
656;;  in this format if positional is true:
657;;
658;;    /* PINNAME */ NETNAME
659;;
660;;  otherwise emit:
661;;
662;;      .PINNAME ( NETNAME )
663;;
664(define systemc:display-pin
665    (lambda (pin positional port)
666      (let
667          ((systemc (regexp-exec systemc-reg (cdr pin))))
668          (begin
669            (if positional
670                (begin    ; output a positional port instanace
671                  (display "  /* " port)
672                  (display (car pin) port)  ; add in name for debugging
673                  (display " */ " port )
674                  (display (cdr pin) port))
675                (begin    ; else output a named port instance
676                  (display "." port)
677                  ; Display the escaped version of the identifier
678                  (systemc:display-escaped-identifier (car pin) port)
679                  (display "(" port)
680                  (if systemc
681                    (display (match:substring systemc 1) port)
682                    (systemc:display-escaped-identifier (cdr pin) port))
683                  (display ")" port)))))))
684
685
686
687;;; Highest level function
688;;; Write Structural systemc representation of the schematic
689;;;
690(define systemc
691  (lambda (output-filename)
692    (let ((port (open-output-file output-filename)))
693      (begin
694        (systemc:get-nets-once!)
695	(systemc:write-top-header port)
696;;        (display "***** start write-wires ********")(newline)
697	(systemc:write-wires port)
698;;        (display "***** end write-wires ********")(newline)
699	(systemc:write-continuous-assigns port)
700	(systemc:components packages port)
701	(systemc:write-bottom-footer port)
702	)
703      (close-output-port port)
704      )
705    )
706)
707
708