-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathhardware.scm
1256 lines (1077 loc) · 40.5 KB
/
hardware.scm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
; Hardware descriptions.
; Copyright (C) 2000, 2009 Red Hat, Inc.
; This file is part of CGEN.
; See file COPYING.CGEN for details.
; This is the base class for all hardware descriptions.
; The actual hardware objects inherit from this (e.g. register, immediate).
; This is used to describe registers, memory, and immediates.
; ??? Maybe other things as well, but this is all that's needed at present.
; ??? Eventually rename to <hardware> but not yet.
(define <hardware-base>
(class-make '<hardware-base>
'(<ident>)
'(
; Name used in semantics.
; This is for cases where a particular hardware element is
; sufficiently different on different mach's of an architecture
; that it is defined separately for each case. The semantics
; refer to this name (which means that one must use a different
; mechanism if one wants both machs in the same semantic code).
sem-name
; The type, an object of class <array>.
; (mode + scalar or vector length)
type
; Indexing support.
; An object of class <hw-asm>, or a subclass of it, or
; #f if there is no special indexing support.
; For register banks, a table of register names.
; ??? Same class as VALUES.
; ??? There are currently no descriptions that require both an
; INDICES and a VALUES specification. It might make sense to
; combine them (which is how things used to be), but it is odd
; to have them combined.
(indices . #f)
; Table of values.
; An object of class <hw-asm>, or a subclass of it, or
; #f if there is no special values support.
; For immediates with special names, a table of names.
; ??? Same class as INDICES.
(values . #f)
; Associative list of (symbol . "handler") entries.
; Each entry maps an operation to its handler (which is up to
; the application but is generally a function name).
(handlers . ())
; Get/set handlers or #f to use the default.
(get . #f)
(set . #f)
; Associative list of get/set handlers for each supported mode,
; or #f to use the default.
; ??? An interesting idea, but not sure it's the best way
; to go. Another way is to explicitly handle it in the insn
; [complicates the RTL]. Another way is to handle this in
; operand get/set handlers. Another way is to have virtual
; regs for each non-default mode. Not sure which is better.
;(getters . #f)
;(setters . #f)
; List of <isa> objects that use this hardware element
; or #f if not computed yet.
; This is a derived from the ISA attribute and is for speed.
(isas-cache . #f)
; Flag indicates whether this hw has been used in a (delay ...)
; rtl expression
(used-in-delay-rtl? . #f)
)
nil)
)
; Accessors
(define-getters <hardware-base> hw
(sem-name type indices values handlers
; ??? These might be more properly named hw-get/hw-set, but those names
; seem ambiguous.
(get . getter) (set . setter)
isas-cache used-in-delay-rtl?)
)
; Mode,rank,shape support.
(method-make-forward! <hardware-base> 'type '(get-mode get-rank get-shape get-num-elms))
(define (hw-mode hw) (send hw 'get-mode))
(define (hw-rank hw) (send hw 'get-rank))
(define (hw-shape hw) (send hw 'get-shape))
(define (hw-num-elms hw) (send hw 'get-num-elms))
; Return default mode to reference HW in.
(define (hw-default-mode hw)
(hw-mode hw)
)
; Return a boolean indicating if X is a hardware object.
; ??? <hardware-base> to be renamed <hardware> in time.
(define (hardware? x) (class-instance? <hardware-base> x))
; Return #t if HW is a scalar.
(define (hw-scalar? hw) (= (hw-rank hw) 0))
; Return number of bits in an element of HW.
(define (hw-bits hw)
(type-bits (hw-type hw))
)
; Generate the name of the enum for hardware object HW.
; This uses the semantic name, not obj:name.
; If HW is a symbol, it is already the semantic name.
(define (hw-enum hw)
(if (symbol? hw)
(string-upcase (string-append "HW_" (gen-c-symbol hw)))
(string-upcase (string-append "HW_" (gen-c-symbol (hw-sem-name hw)))))
)
; Return a boolean indicating if it's ok to reference SELF in mode
; NEW-MODE-NAME, index INDEX.
; Hardware types are required to override this method.
; VOID and DFLT are never valid for NEW-MODE-NAME.
(method-make!
<hardware-base> 'mode-ok?
(lambda (self new-mode-name index)
(error "mode-ok? method not overridden:" (obj:name self)))
)
(define (hw-mode-ok? hw new-mode-name index)
(send hw 'mode-ok? new-mode-name index)
)
; Return mode to use for the index or #f if scalar.
(method-make!
<hardware-base> 'get-index-mode
(lambda (self)
(error "get-index-mode method not overridden:" (obj:name self)))
)
(define (hw-index-mode hw) (send hw 'get-index-mode))
; Compute the isas used by HW and cache the results.
(method-make!
<hardware-base> 'get-isas
(lambda (self)
(or (elm-get self 'isas-cache)
(let* ((isas (obj-attr-value self 'ISA))
(isa-objs (if (equal? isas '(all)) (current-isa-list)
(map current-isa-lookup isas))))
(elm-set! self 'isas-cache isa-objs)
isa-objs)))
)
(define (hw-isas hw) (send hw 'get-isas))
; Was this hardware used in a (delay ...) rtl expression?
(method-make!
<hardware-base> 'used-in-delay-rtl?
(lambda (self) (elm-get self 'used-in-delay-rtl?))
)
(define (hw-used-in-delay-rtl? hw) (send hw 'used-in-delay-rtl?))
; FIXME: replace pc?,memory?,register?,iaddress? with just one method.
; Return boolean indicating if hardware element is the PC.
(method-make! <hardware-base> 'pc? (lambda (self) #f))
; Return boolean indicating if hardware element is some kind of memory.
; ??? Need to allow multiple kinds of memory and therefore need to allow
; .cpu files to specify this (i.e. an attribute). We could use has-attr?
; here, or we could have the code that creates the object override this
; method if the MEMORY attribute is present.
; ??? Could also use a member instead of a method.
(method-make! <hardware-base> 'memory? (lambda (self) #f))
(define (memory? hw) (send hw 'memory?))
; Return boolean indicating if hardware element is some kind of register.
(method-make! <hardware-base> 'register? (lambda (self) #f))
(define (register? hw) (send hw 'register?))
; Return boolean indicating if hardware element is an address.
(method-make! <hardware-base> 'address? (lambda (self) #f))
(method-make! <hardware-base> 'iaddress? (lambda (self) #f))
(define (address? hw) (send hw 'address?))
(define (iaddress? hw) (send hw 'iaddress?))
; Assembler support.
; Baseclass.
(define <hw-asm>
(class-make '<hw-asm> '(<ident>)
'(
; The <mode> object of the mode to use.
; A copy of the object's mode if we're in the "values"
; member. If we're in the "indices" member this is typically
; UINT.
mode
)
nil)
)
; Keywords.
; Keyword lists associate a name with a number and are used for things
; like register name tables (the `indices' field of a hw spec) and
; immediate value tables (the `values' field of a hw spec).
;
; TODO: For things like the sparc fp regs, have a quasi-keyword that is
; prefix plus number. This will save having to create a table of each
; register name.
(define <keyword>
(class-make '<keyword> '(<hw-asm>)
'(
; Prefix value to pass to the corresponding enum.
enum-prefix
; Prefix of each name in VALUES, as a string.
; This is *not* prepended to each name in the enum.
name-prefix
; Associative list of values.
; Each element is (name value [attrs]).
; ??? May wish to allow calling a function to compute the
; value at runtime.
values
)
nil)
)
; Accessors
(define-getters <keyword> kw (mode enum-prefix name-prefix values))
; Parse a keyword spec.
;
; ENUM-PREFIX is for the corresponding enum.
; The syntax of VALUES is: (prefix ((name1 [value1 [(attr-list1)]]) ...))
; NAME-PREFIX is a prefix added to each value's name in the generated
; lookup table.
; Each value is a number of mode MODE, the name of the mode.
; ??? We have no problem handling any kind of number, we're Scheme.
; However, it's not clear yet how applications will want to handle it, but
; that is left to the application. Still, it might be preferable to impose
; some restrictions which can later be relaxed as necessary.
; ??? It would be useful to have two names for each value: asm name, enum name.
(define (/keyword-parse context name comment attrs mode enum-prefix
name-prefix values)
;; Pick out name first to augment the error context.
(let* ((name (parse-name context name))
(context (context-append-name context name))
(enum-prefix (or enum-prefix
(if (equal? (cgen-rtl-version) '(0 7))
(string-upcase (->string name))
(string-append ;; default to NAME-
(string-upcase (->string name))
"-")))))
;; FIXME: parse values.
(let ((result (make <keyword>
(parse-name context name)
(parse-comment context comment)
(atlist-parse context attrs "")
(parse-mode-name (context-append context ": mode") mode)
(parse-string (context-append context ": enum-prefix")
enum-prefix)
(parse-string (context-append context ": name-prefix")
name-prefix)
values)))
result))
)
; Read a keyword description
; This is the main routine for analyzing a keyword description in the .cpu
; file.
; CONTEXT is a <context> object for error messages.
; ARG-LIST is an associative list of field name and field value.
; /keyword-parse is invoked to create the <keyword> object.
(define (/keyword-read context . arg-list)
(let (
(name #f)
(comment "")
(attrs nil)
(mode 'INT)
(enum-prefix #f) ;; #f indicates "not set"
(name-prefix "")
(values nil)
)
; Loop over each element in ARG-LIST, recording what's found.
(let loop ((arg-list arg-list))
(if (null? arg-list)
nil
(let ((arg (car arg-list))
(elm-name (caar arg-list)))
(case elm-name
((name) (set! name (cadr arg)))
((comment) (set! comment (cadr arg)))
((attrs) (set! attrs (cdr arg)))
((mode) (set! mode (cadr arg)))
((print-name)
;; Renamed to enum-prefix in rtl version 0.8.
(if (not (equal? (cgen-rtl-version) '(0 7)))
(parse-error context "print-name renamed to enum-prefix" arg))
(set! enum-prefix (cadr arg)))
((enum-prefix)
;; enum-prefix added in rtl version 0.8.
(if (and (= (cgen-rtl-major) 0)
(< (cgen-rtl-minor) 8))
(parse-error context "invalid hardware arg" arg))
(set! enum-prefix (cadr arg)))
((prefix)
;; Renamed to name-prefix in rtl version 0.8.
(if (not (equal? (cgen-rtl-version) '(0 7)))
(parse-error context "prefix renamed to name-prefix" arg))
(set! name-prefix (cadr arg)))
((name-prefix)
;; name-prefix added in rtl version 0.8.
(if (and (= (cgen-rtl-major) 0)
(< (cgen-rtl-minor) 8))
(parse-error context "invalid hardware arg" arg))
(set! name-prefix (cadr arg)))
((values) (set! values (cdr arg)))
(else (parse-error context "invalid hardware arg" arg)))
(loop (cdr arg-list)))))
; Now that we've identified the elements, build the object.
(/keyword-parse context name comment attrs mode
enum-prefix name-prefix values))
)
; Define a keyword object, name/value pair list version.
(define define-keyword
(lambda arg-list
(let ((kw (apply /keyword-read (cons (make-current-context "define-keyword")
arg-list))))
(if kw
(begin
(current-kw-add! kw)
; Define an enum so the values are usable everywhere.
; One use is giving names to register numbers and special constants
; to make periphery C/C++ code more legible.
; FIXME: Should pass on mode to enum.
(define-full-enum (obj:name kw) (obj:comment kw)
(atlist-source-form (obj-atlist kw))
(if (and (= (cgen-rtl-major) 0)
(< (cgen-rtl-minor) 8))
;; Prior to rtl version 0.8 we up-cased the prefix here
;; and added the trailing - ourselves.
(string-upcase (string-append (kw-enum-prefix kw) "-"))
(kw-enum-prefix kw))
(kw-values kw))))
kw))
)
; Parsing support.
; List of hardware types.
; This maps names in the `type' entry of define-hardware to the class name.
(define /hardware-types
'((register . <hw-register>)
(pc . <hw-pc>)
(memory . <hw-memory>)
(immediate . <hw-immediate>)
(address . <hw-address>)
(iaddress . <hw-iaddress>))
)
; Parse an inline keyword spec.
; These are keywords defined inside something else.
; CONTAINER is the <ident> object of the container.
; MODE is the name of the mode.
(define (/hw-parse-keyword context args container mode)
(if (!= (length args) 2)
(parse-error context "invalid keyword spec" args))
; Name, comment, and attributes are copied from our container object.
; They're needed to output the table.
; ??? This isn't quite right as some day a container may contain multiple
; keyword instances. To be fixed in time.
(/keyword-parse context (obj:name container) (obj:comment container)
;; PRIVATE: keyword table is implicitly defined, it isn't
;; accessible with current-kw-lookup.
(cons 'PRIVATE (atlist-source-form (obj-atlist container)))
mode
;; This is unused, use a magic value to catch any uses.
"UNUSED"
(car args) ; prefix
(cadr args)) ; value
)
; Parse an indices spec.
; CONTAINER is the <ident> object of the container.
; Currently there is only special support for keywords.
; Otherwise MODE is used. MODE is the name, not a <mode> object.
; The syntax is: (keyword keyword-spec) - see <keyword> for details.
(define (/hw-parse-indices context indices container mode)
(if (null? indices)
(make <hw-asm>
(obj:name container) (obj:comment container) (obj-atlist container)
(parse-mode-name (context-append context ": mode") mode))
(begin
(if (not (list? indices))
(parse-error context "invalid indices spec" indices))
(case (car indices)
((keyword) (/hw-parse-keyword context (cdr indices) container mode))
((extern-keyword) (begin
(if (null? (cdr indices))
(parse-error context "missing keyword name"
indices))
(let ((kw (current-kw-lookup (cadr indices))))
(if (not kw)
(parse-error context "unknown keyword"
indices))
kw)))
(else (parse-error context "unknown indices type" (car indices))))))
)
; Parse a values spec.
; CONTAINER is the <ident> object of the container.
; Currently there is only special support for keywords.
; Otherwise MODE is used. MODE is the name, not a <mode> object.
; The syntax is: (keyword keyword-spec) - see <keyword> for details.
(define (/hw-parse-values context values container mode)
(if (null? values)
(make <hw-asm>
(obj:name container) (obj:comment container) (obj-atlist container)
(parse-mode-name (context-append context ": mode") mode))
(begin
(if (not (list? values))
(parse-error context "invalid values spec" values))
(case (car values)
((keyword) (/hw-parse-keyword context (cdr values) container mode))
((extern-keyword) (begin
(if (null? (cdr values))
(parse-error context "missing keyword name"
values))
(let ((kw (current-kw-lookup (cadr values))))
(if (not kw)
(parse-error context "unknown keyword"
values))
kw)))
(else (parse-error context "unknown values type" (car values))))))
)
; Parse a handlers spec.
; Each element is (name "string").
(define (/hw-parse-handlers context handlers)
(parse-handlers context '(parse print) handlers)
)
; Parse a getter spec.
; The syntax is (([index]) (expression)).
; Omit `index' for scalar objects.
; Externally they're specified as `get'. Internally we use `getter'.
(define (/hw-parse-getter context getter scalar?)
(if (null? getter)
#f ; use default
(let ((valid "((index) (expression))")
(scalar-valid "(() (expression))"))
(if (or (not (list? getter))
(!= (length getter) 2)
(not (and (list? (car getter))
(= (length (car getter)) (if scalar? 0 1)))))
(parse-error context
(string-append "invalid getter, should be "
(if scalar? scalar-valid valid))
getter))
(if (not (rtx? (cadr getter)))
(parse-error context "invalid rtx expression" getter))
getter))
)
; Parse a setter spec.
; The syntax is (([index] newval) (expression)).
; Omit `index' for scalar objects.
; Externally they're specified as `set'. Internally we use `setter'.
(define (/hw-parse-setter context setter scalar?)
(if (null? setter)
#f ; use default
(let ((valid "((index newval) (expression))")
(scalar-valid "((newval) (expression))"))
(if (or (not (list? setter))
(!= (length setter) 2)
(not (and (list? (car setter))
(= (length (car setter)) (if scalar? 1 2)))))
(parse-error context
(string-append "invalid setter, should be "
(if scalar? scalar-valid valid))
setter))
(if (not (rtx? (cadr setter)))
(parse-error context "invalid rtx expression" setter))
setter))
)
; Parse hardware description
; This is the main routine for building a hardware object from a hardware
; description in the .cpu file.
; All arguments are in raw (non-evaluated) form.
; The result is the parsed object or #f if object isn't for selected mach(s).
;
; ??? Might want to redo to handle hardware type specific specs more cleanly.
; E.g. <hw-immediate> shouldn't have to see get/set specs.
(define (/hw-parse context name comment attrs semantic-name type
indices values handlers get set layout)
(logit 2 "Processing hardware element " name " ...\n")
(if (null? type)
(parse-error context "missing hardware type" name))
;; Pick out name first to augment the error context.
(let* ((name (parse-name context name))
(context (context-append-name context name))
(class-name (assq-ref /hardware-types (car type)))
(atlist-obj (atlist-parse context attrs "cgen_hw")))
(if (not class-name)
(parse-error context "unknown hardware type" type))
(if (keep-atlist? atlist-obj #f)
(let ((result (new (class-lookup class-name))))
(send result 'set-name! name)
(send result 'set-comment! (parse-comment context comment))
(send result 'set-atlist! atlist-obj)
(elm-xset! result 'sem-name semantic-name)
(send result 'parse! context
(cdr type) indices values handlers get set layout)
; If this is a virtual reg, get/set specs must be provided.
(if (and (obj-has-attr? result 'VIRTUAL)
(not (and (hw-getter result) (hw-setter result))))
(parse-error context "virtual reg requires get/set specs" name))
; If get or set specs are specified, can't have CACHE-ADDR.
(if (and (obj-has-attr? result 'CACHE-ADDR)
(or (hw-getter result) (hw-setter result)))
(parse-error context "can't have CACHE-ADDR with get/set specs"
name))
result)
(begin
(logit 2 "Ignoring " name ".\n")
#f)))
)
; Read a hardware description
; This is the main routine for analyzing a hardware description in the .cpu
; file.
; CONTEXT is a <context> object for error messages.
; ARG-LIST is an associative list of field name and field value.
; /hw-parse is invoked to create the <hardware> object.
(define (/hw-read context . arg-list)
(let (
(name nil)
(comment "")
(attrs nil)
(semantic-name nil) ; name used in semantics, default is `name'
(type nil) ; hardware type (register, immediate, etc.)
(indices nil)
(values nil)
(handlers nil)
(get nil)
(set nil)
(layout nil)
)
; Loop over each element in ARG-LIST, recording what's found.
(let loop ((arg-list arg-list))
(if (null? arg-list)
nil
(let ((arg (car arg-list))
(elm-name (caar arg-list)))
(case elm-name
((name) (set! name (cadr arg)))
((comment) (set! comment (cadr arg)))
((attrs) (set! attrs (cdr arg)))
((semantic-name) (set! semantic-name (cadr arg)))
((type) (set! type (cdr arg)))
((indices) (set! indices (cdr arg)))
((values) (set! values (cdr arg)))
((handlers) (set! handlers (cdr arg)))
((get) (set! get (cdr arg)))
((set) (set! set (cdr arg)))
((layout) (set! layout (cdr arg)))
(else (parse-error context "invalid hardware arg" arg)))
(loop (cdr arg-list)))))
; Now that we've identified the elements, build the object.
(/hw-parse context name comment attrs
(if (null? semantic-name) name semantic-name)
type indices values handlers get set layout))
)
; Define a hardware object, name/value pair list version.
(define define-hardware
(lambda arg-list
(let ((hw (apply /hw-read (cons (make-current-context "define-hardware")
arg-list))))
(if hw
(current-hw-add! hw))
hw))
)
; Define a hardware object, all arguments specified.
(define (define-full-hardware name comment attrs semantic-name type
indices values handlers get set layout)
(let ((hw (/hw-parse (make-current-context "define-full-hardware")
name comment attrs semantic-name type
indices values handlers get set layout)))
(if hw
(current-hw-add! hw))
hw)
)
; Main routine for modifying existing definitions.
(define modify-hardware
(lambda arg-list
(let ((context (make-current-context "modify-hardware")))
; FIXME: Experiment. This implements the :name/value style by
; converting it to (name value). In the end there shouldn't be two
; styles. People might prefer :name/value, but it's not as amenable
; to macro processing (insert potshots regarding macro usage).
(if (keyword-list? (car arg-list))
(set! arg-list (keyword-list->arg-list arg-list)))
; First find out which element.
; There's no requirement that the name be specified first.
(let ((hw-spec (assq 'name arg-list)))
(if (not hw-spec)
(parse-error context "hardware name not specified" arg-list))
(let ((hw (current-hw-lookup (arg-list-symbol-arg context hw-spec))))
(if (not hw)
(parse-error context "undefined hardware element" hw-spec))
; Process the rest of the args now that we have the affected object.
(let loop ((args arg-list))
(if (null? args)
#f ; done
(let ((arg-spec (car args)))
(case (car arg-spec)
((name) #f) ; ignore, already processed
((add-attrs)
(let ((atlist-obj (atlist-parse context (cdr arg-spec)
"cgen_hw")))
; prepend attrs so new ones override existing ones
(obj-prepend-atlist! hw atlist-obj)))
(else
(parse-error context "invalid/unsupported option"
(car arg-spec))))
(loop (cdr args))))))))
*UNSPECIFIED*)
)
; Lookup a hardware object using its semantic name.
; The result is a list of elements with SEM-NAME.
; Callers must deal with cases where there is more than one.
(define (current-hw-sem-lookup sem-name)
(find (lambda (hw) (eq? (hw-sem-name hw) sem-name))
(current-hw-list))
)
; Same as current-hw-sem-lookup, but result is 1 hw element or #f if not
; found. An error is signalled if multiple hw elements are found.
(define (current-hw-sem-lookup-1 sem-name)
(let ((hw-objs (current-hw-sem-lookup sem-name)))
(case (length hw-objs)
((0) #f)
((1) (car hw-objs))
(else (error "ambiguous hardware reference" sem-name))))
)
; Basic hardware types.
; These inherit from `hardware-base'.
; ??? Might wish to allow each target to add more, but we provide enough
; examples to cover most cpus.
; A register (or an array of them).
(define <hw-register> (class-make '<hw-register> '(<hardware-base>) nil nil))
; Subroutine of -hw-create-[gs]etter-from-layout to validate a layout.
; Valid values:
; - 0 or 1
; - (value length)
; - hardware-name
(define (/hw-validate-layout context layout width)
(if (not (list? layout))
(parse-error context "layout is not a list" layout))
(let loop ((layout layout) (shift 0))
(if (null? layout)
(begin
; Done. Now see if number of bits in layout matches total width.
(if (not (= shift width))
(parse-error context (string-append
"insufficient number of bits (need "
(number->string width)
")")
shift)))
; Validate next entry.
(let ((val (car layout)))
(cond ((number? val)
(if (not (memq val '(0 1)))
(parse-error context
"non 0/1 layout entry requires length"
val))
(loop (cdr layout) (1+ shift)))
((pair? val)
(if (or (not (number? (car val)))
(not (pair? (cdr val)))
(not (number? (cadr val)))
(not (null? (cddr val))))
(parse-error context
"syntax error in layout, expecting `(value length)'"
val))
(loop (cdr layout) (+ shift (cadr val))))
((symbol? val)
(let ((hw (current-hw-lookup val)))
(if (not hw)
(parse-error context "unknown hardware element" val))
(if (not (hw-scalar? hw))
(parse-error context "non-scalar hardware element" val))
(loop (cdr layout)
(+ shift (hw-bits hw)))))
(else
(parse-error context "bad layout element" val))))))
*UNSPECIFIED*
)
; Return the getter spec to use for LAYOUT.
; WIDTH is the width of the combined value in bits.
;
; Example:
; Assuming h-hw[123] are 1 bit registers, and width is 32
; given ((0 29) h-hw1 h-hw2 h-hw3), return
; (()
; (or SI (sll SI (zext SI (reg h-hw1)) 2)
; (or SI (sll SI (zext SI (reg h-hw2)) 1)
; (zext SI (reg h-hw3)))))
(define (/hw-create-getter-from-layout context layout width)
(let ((add-to-res (lambda (result mode-name val shift)
(if (null? result)
(rtx-make 'sll mode-name val shift)
(rtx-make 'or mode-name
(rtx-make 'sll mode-name
(rtx-make 'zext mode-name val)
shift)
result))))
(mode-name (obj:name (mode-find width 'UINT))))
(let loop ((result nil) (layout (reverse layout)) (shift 0))
(if (null? layout)
(list nil result) ; getter spec: (get () (expression))
(let ((val (car layout)))
(cond ((number? val)
; ignore if zero
(if (= val 0)
(loop result (cdr layout) (1+ shift))
(loop (add-to-res result mode-name val shift)
(cdr layout)
(1+ shift))))
((pair? val)
; ignore if zero
(if (= (car val) 0)
(loop result (cdr layout) (+ shift (cadr val)))
(loop (add-to-res result mode-name (car val) shift)
(cdr layout)
(+ shift (cadr val)))))
((symbol? val)
(let ((hw (current-hw-lookup val)))
(loop (add-to-res result mode-name
(rtx-make 'reg val)
shift)
(cdr layout)
(+ shift (hw-bits hw)))))
(else
(assert (begin "bad layout element" #f))))))))
)
; Return the setter spec to use for LAYOUT.
; WIDTH is the width of the combined value in bits.
;
; Example:
; Assuming h-hw[123] are 1 bit registers,
; given (h-hw1 h-hw2 h-hw3), return
; ((val)
; (sequence ()
; (set (reg h-hw1) (and (srl val 2) 1))
; (set (reg h-hw2) (and (srl val 1) 1))
; (set (reg h-hw3) (and (srl val 0) 1))
; ))
(define (/hw-create-setter-from-layout context layout width)
(let ((mode-name (obj:name (mode-find width 'UINT))))
(let loop ((sets nil) (layout (reverse layout)) (shift 0))
(if (null? layout)
(list '(val) ; setter spec: (set (val) (expression))
(apply rtx-make (cons 'sequence (cons nil sets))))
(let ((val (car layout)))
(cond ((number? val)
(loop sets (cdr layout) (1+ shift)))
((pair? val)
(loop sets (cdr layout) (+ shift (cadr val))))
((symbol? val)
(let ((hw (current-hw-lookup val)))
(loop (cons (rtx-make 'set
(rtx-make 'reg val)
(rtx-make 'and
(rtx-make 'srl 'val shift)
(1- (logsll 1 (hw-bits hw)))))
sets)
(cdr layout)
(+ shift (hw-bits hw)))))
(else
(assert (begin "bad layout element" #f))))))))
)
; Parse a register spec.
; .cpu syntax: (register mode [(dimension)])
; or: (register (mode bits) [(dimension)])
(method-make!
<hw-register> 'parse!
(lambda (self context type indices values handlers getter setter layout)
(if (or (null? type)
(> (length type) 2))
(parse-error context "invalid register spec" type))
(if (and (= (length type) 2)
(or (not (list? (cadr type)))
(> (length (cadr type)) 1)))
(parse-error context "bad register dimension spec" type))
; Must parse and set type before analyzing LAYOUT.
(elm-set! self 'type (parse-type context type))
; LAYOUT is a shorthand way of specifying getter/setter specs.
; For registers that are just a collection of other registers
; (e.g. the status register in mips), it's easier to specify the
; registers that make up the bigger register, rather than to specify
; get/set specs.
; We don't override any provided get/set specs though.
(if (not (null? layout))
(let ((width (hw-bits self)))
(/hw-validate-layout context layout width)
(if (null? getter)
(set! getter
(/hw-create-getter-from-layout context layout width)))
(if (null? setter)
(set! setter
(/hw-create-setter-from-layout context layout width)))
))
(elm-set! self 'indices (/hw-parse-indices context indices self 'UINT))
(elm-set! self 'values (/hw-parse-values context values self
(obj:name (send (elm-get self 'type)
'get-mode))))
(elm-set! self 'handlers (/hw-parse-handlers context handlers))
(elm-set! self 'get (/hw-parse-getter context getter (hw-scalar? self)))
(elm-set! self 'set (/hw-parse-setter context setter (hw-scalar? self)))
*UNSPECIFIED*)
)
; Return boolean indicating if hardware element is some kind of register.
(method-make! <hw-register> 'register? (lambda (self) #t))
; Return a boolean indicating if it's ok to reference SELF in mode
; NEW-MODE-NAME, index INDEX.
;
; ??? INDEX isn't currently used. The intent is to use it if it's a known
; value, and otherwise assume for our purposes it's valid and leave any
; further error checking to elsewhere.
;
; ??? This method makes more sense if we support multiple modes via
; getters/setters. Maybe we will some day, so this is left as is for now.
(method-make!
<hw-register> 'mode-ok?
(lambda (self new-mode-name index)
(let ((cur-mode (send self 'get-mode))
(new-mode (mode:lookup new-mode-name)))
(if (mode:eq? new-mode-name cur-mode)
#t
; ??? Subject to revisiting.
; Only allow floats if same mode (which is handled above).
; Only allow non-widening if ints.
; On architectures where shortening/widening can refer to a
; quasi-different register, it is up to the target to handle this.
; See the comments for the getter/setter/getters/setters class
; members.
(let ((cur-mode-class (mode:class cur-mode))
(cur-bits (mode:bits cur-mode))
(new-mode-class (mode:class new-mode))
(new-bits (mode:bits new-mode)))
; Compensate for registers defined with an unsigned mode.
(if (eq? cur-mode-class 'UINT)
(set! cur-mode-class 'INT))
(if (eq? new-mode-class 'UINT)
(set! new-mode-class 'INT))
(if (eq? cur-mode-class 'INT)
(and (eq? new-mode-class cur-mode-class)
(<= new-bits cur-bits))
#f)))))
)
; Return mode to use for the index or #f if scalar.
(method-make!
<hw-register> 'get-index-mode
(lambda (self)
(if (scalar? (hw-type self))
#f
UINT))
)
; The program counter (PC) hardware register.
; This is a separate class as the simulator needs a place to put special
; get/set methods.
(define <hw-pc> (class-make '<hw-pc> '(<hw-register>) nil nil))
; Parse a pc spec.
(method-make!
<hw-pc> 'parse!
(lambda (self context type indices values handlers getter setter layout)
(if (not (null? type))
(elm-set! self 'type (parse-type context type))
(elm-set! self 'type (make <scalar> (mode:lookup 'IAI))))
(if (not (null? indices))
(parse-error context "indices specified for pc" indices))
(if (not (null? values))
(parse-error context "values specified for pc" values))
(if (not (null? layout))
(parse-error context "layout specified for pc" values))
; The initial value of INDICES, VALUES is #f which is what we want.
(elm-set! self 'handlers (/hw-parse-handlers context handlers))
(elm-set! self 'get (/hw-parse-getter context getter (hw-scalar? self)))
(elm-set! self 'set (/hw-parse-setter context setter (hw-scalar? self)))
*UNSPECIFIED*)
)
; Indicate we're the pc.
(method-make! <hw-pc> 'pc? (lambda (self) #t))
(define (hw-pc? hw) (send hw 'pc?))
; Memory.
(define <hw-memory> (class-make '<hw-memory> '(<hardware-base>) nil nil))
; Parse a memory spec.
; .cpu syntax: (memory mode [(dimension)])
; or: (memory (mode bits) [(dimension)])
(method-make!
<hw-memory> 'parse!
(lambda (self context type indices values handlers getter setter layout)
(if (or (null? type)
(> (length type) 2))
(parse-error context "invalid memory spec" type))
(if (and (= (length type) 2)