Forum: FPGA, VHDL & Co. Vivado: Debugging von tcl-Skripten


von Duke Scarring (Gast)


Angehängte Dateien:

Lesenswert?

Ich bin gerade dabei von ISE auf Vivado umzusteigen.
Und weil natürlich die Befehle "write_mem_info" und "write_bmm" für 
inferierte (B)RAMs nicht funktionieren, bin ich jetzt drauf angewiesen 
mir einen Satz .mmi-Files zu erstellen.

Dabei wollte ich die Hilfe von "bmm2mmi.tcl" in Anspruch nehmen.
Leider bekomme ich bei der Befehlsausführung (Vivado Tcl-Console) eine 
Fehlermeldung:
1
bmm2mmi 8192x32.bmm
2
can't read "1": no such variable
Nun wäre es schön zu Erfahren, in welcher Quellcodezeile das Problem 
liegt.
Ich kann das Skript mit printf^Wputs debuggen, aber vielleicht gibt es 
noch einen eleganteren Weg.

Kenn jemand einen?

Hinweise, wie man das .mmi-File gestaltet, wenn die Synthese der Meinung 
ist, die Paritybits mit benutzen zu müssen, sind auch willkommen...

Duke

von Markus F. (mfro)


Lesenswert?

Duke Scarring schrieb:
> ...
> Nun wäre es schön zu Erfahren, in welcher Quellcodezeile das Problem
> liegt.

das ist relativ einfach, weil $1 in der Datei nur dreimal vorkommt. 
Findest Du sicher selbst.

Daß es überhaupt vorkommt, läßt mich glauben, daß das tcl Script 
schlicht nicht dazu gedacht ist, es direkt aus der tcl Konsole 
aufzurufen, sondern irgendwie noch eine Art Vorverarbeitung braucht.

Kann es sein, daß es da noch ein bash Script "drumrum" gibt, das diese 
Vorverarbeitung macht? Ich bin jetzt nicht unbedingt der tcl-Profi, aber 
bislang sind mir jedenfalls noch keine tcl Scripts untergekommen, die 
mit $1, $2, usw. umgegangen wären...

von Julius J. (joe_joule)


Lesenswert?

deine "1" Variable ist der erste (sub-)match einer regexp, entweder in 
der Funktion "get_bit_lanes" oder in "bram_info". Wenn diese regexp 
nichts findet, existieren die Variablen "1" bzw. "2" (und auch "all") 
nicht.
Ich weiß nicht was ISE oder Vivado oder bmm etc. ist, daher kann ich 
hier nicht weiter helfen, aber schau doch mal ob der Input den die 
Funktionen kriegen (Textfiles?) zu den regular expressions passen.

von Markus F. (mfro)


Lesenswert?

Achso - der regexp-Aufruf belegt die Variablen!

Danke - wieder was gelernt.

von Julius J. (joe_joule)


Lesenswert?

Um zu sehen was schief läuft, kann man die Funktionen um eine Ausgabe 
("puts") erweitern:
1
proc get_bit_lanes {string type} {
2
  puts "string: '$string'"
3
  set bit_lane [regexp {\[(.+)\]} $string all 1 2]
4
...
1
proc bram_info {bram type} {
2
  puts "bram: '$bram'"
3
  set temp [get_property bmm_info_memory_device [get_cells $bram]]
4
  puts "temp: '$temp'"
5
  set bmm_info_memory_device [regexp {\[(.+)\]\[(.+)\]} $temp all 1 2]
6
...

von Duke Scarring (Gast)


Lesenswert?

Ja, der regexp macht das Problem.
Offensichtlich ist dieses Skript nur für eine ganz bestimmte .bmm-Syntax 
geschrieben. Kaum ist mal ein Leerzeichen mehr oder weniger drin, geht 
es schief.

Naja, mein erstes .mmi-File hab ich jetzt. Ich werde die weiteren aus 
diesem ableiten. Wenn die alle .mmi-Files funktionieren, muß der 
Algorithmus in write_mmi.tcl implementiert werden, damit nach jedem 
Synthesedurchlauf auch ein aktualisiertes .mmi-File für updatemem 
bereitsteht.

Duke

von Duke Scarring (Gast)


Lesenswert?

Schritt 2: Die Sache mit den .mmi-Files ist grottig, ein paar gehen, 
aber sonst:
- keine Unterstützung der Paritybits oder
- Segmentation fault oder
- EXCEPTION_ACCESS_VIOLATION

Das ging mit data2mem und .bmm-Dateien schon mal besser.
Und siehe da, data2mem gibt es auch in Vivado noch (jetzt im SDK):
1
$ find  /opt/ -name data2mem
2
/opt/Xilinx/SDK/2016.4/bin/data2mem
3
/opt/Xilinx/SDK/2016.4/bin/unwrapped/lnx64.o/data2mem

Weiterentwickelt wurde es offensichtlich nicht:
1
Release 14.6 - Data2MEM P_INT.20161201, build 3.0.10 Apr 3, 2013
2
Copyright (c) 1995-2017 Xilinx, Inc.  All rights reserved.

Sei's drum. Hier ist ein TCL-Skript, was nach der Synthese aufgerufen 
wird (Vivado TCL-Console) und ein fertiges .bmm-File generiert. Als 
Parameter kann eine Instanz mitgegeben werden.

bekannte Einschränkungen:
- funktioniert für 1k*32 Bit bis 64k*32 Bit
- der RAM darf nicht direkt im top-Modul inferiert werden
- ob dual-port geht wurde nicht getestet
- für die Größen 1k*32, 2k*32 und 4k*32 braucht man angepasste 
.mem-Dateien , siehe: 
Beitrag "Re: zpu, data2mem, RAM layout bei der Xilinx 7 Series"
- getestet mit Vivado v2014.4 (64-bit) und data2mem 14.6
- der Speicher wurde inferiert und für einen Kintex-7 synthetisiert

1
# generate a BMM file, catch all information from BRAM properties
2
proc generate_one_bmm_file { instance } {
3
    
4
    if {$instance == ""} {
5
        set bram_unsort_list [get_cells -hierarchical -filter { LOC =~ "RAMB*" }]
6
    } else {
7
        set bram_unsort_list [get_cells -hierarchical -filter "NAME =~ $instance/* && LOC =~ RAMB*"]
8
    }
9
    # generate list of used BRAM on this instance
10
    set bram_list [lsort -dictionary -decreasing $bram_unsort_list]
11
    set bram_instance_name [lindex [split [lindex $bram_list 0] "/"] 0]
12
    # count instances
13
    set bram_count [llength $bram_list]
14
    set bram_length [expr $bram_count * 1024]
15
    set bram_bytes [expr $bram_length * 4]
16
    # defaults
17
    set bram_width 0
18
    set bram_words $bram_bytes
19
    set bram_divisor 1
20
21
    # synthesis specific corrections
22
    if       {$bram_count == 128} {
23
        set bram_divisor 4
24
    } elseif {$bram_count ==  64} {
25
        set bram_divisor 2
26
    } elseif {$bram_count ==  32} {
27
    } elseif {$bram_count ==  16} {
28
    } elseif {$bram_count ==   8} {
29
    } elseif {$bram_count ==   4} {
30
    } elseif {$bram_count ==   2} {
31
        set bram_words [expr $bram_bytes / 2]
32
    } elseif {$bram_count ==   1} {
33
        set bram_words [expr $bram_bytes / 4]
34
    } elseif {$bram_count ==   0} {
35
        puts "found no brams in instance $instance"
36
        return
37
    } else {
38
        puts "currently unsupported BRAM size ($instance: $bram_count)"
39
        return
40
    }
41
42
    # format nr. of bytes or words
43
    set hex_start [format %08X 0]
44
    set hex_end   [format %08X [expr $bram_words - 1]]
45
46
    # count bit width over bram instances
47
    foreach bram $bram_list {
48
        set bram_width [expr $bram_width + [get_property READ_WIDTH_A [get_cells $bram]]]
49
    }
50
51
    # fix bit width
52
    set bram_width [expr $bram_width / $bram_divisor]
53
54
    # check width, select memory type
55
    if {$bram_width == 32} {
56
        set mtype "RAMB32"
57
    } elseif {$bram_width == 36} {
58
        set mtype "RAMB36 WORD_ADDRESSING"
59
    } else {
60
        puts "currently unsupported BRAM width ($instance: $bram_width)"
61
        return
62
    }
63
64
    set memory_layout "${bram_length}x${bram_width}"
65
    #puts "memory layout: $memory_layout"
66
67
    # filename
68
    if {$instance == ""} {
69
        set bmm_file_name  "gen_${memory_layout}.bmm"
70
    } else {
71
        set bmm_file_name  "gen_${instance}_${memory_layout}.bmm"
72
    }
73
    # open bmm file for writing
74
    set bmm_file [open $bmm_file_name w]
75
76
    if {$bram_divisor > 1} {
77
        # build COMBINED rams   
78
        puts $bmm_file "ADDRESS_SPACE $bram_instance_name COMBINED [0x$hex_start:0x$hex_end]"
79
        for {set range_index 0} {$range_index < $bram_divisor} {incr range_index} {
80
            puts $bmm_file "  ADDRESS_RANGE $mtype"
81
            puts $bmm_file "    BUS_BLOCK"
82
                foreach bram $bram_list {
83
                    # cut instance (e.g. ram_instance/ram_reg_1_2)
84
                    set leaf [lindex [split $bram "/"] 1]
85
                    # get elements (e.g. ram_reg_1_2)
86
                    set elements [split $leaf "_"]
87
                    # get second-to-last element (e.g. 1)
88
                    set range [lindex $elements end-1]
89
                    if {$range == $range_index} {
90
                        set lane_begin [get_property bram_slice_begin [get_cells $bram]]
91
                        set lane_end   [get_property bram_slice_end   [get_cells $bram]]
92
                        set loc        [get_property LOC [get_cells $bram]]
93
                        # cut BEL from LOC
94
                        set loc        [lindex [split $loc "_"] end]
95
                        puts $bmm_file "      $bram [$lane_end:$lane_begin]  LOC=$loc;"
96
                    }
97
                }
98
            puts $bmm_file "    END_BUS_BLOCK;"
99
            puts $bmm_file "  END_ADDRESS_RANGE;"
100
        }
101
        puts $bmm_file "END_ADDRESS_SPACE;"
102
    } else {
103
        # build all other rams
104
        puts $bmm_file "ADDRESS_SPACE $bram_instance_name $mtype [0x$hex_start:0x$hex_end]"
105
        puts $bmm_file "  BUS_BLOCK"
106
            foreach bram $bram_list {
107
                set lane_begin [get_property bram_slice_begin [get_cells $bram]]
108
                set lane_end   [get_property bram_slice_end   [get_cells $bram]]
109
                set loc        [get_property LOC [get_cells $bram]]
110
                # cut BEL from LOC
111
                set loc        [lindex [split $loc "_"] end]
112
                puts $bmm_file "    $bram [$lane_end:$lane_begin]  LOC=$loc;"
113
            }
114
        puts $bmm_file "  END_BUS_BLOCK;"
115
        puts $bmm_file "END_ADDRESS_SPACE;"
116
    }
117
    close $bmm_file
118
    puts "bmm file generated ($bmm_file_name)"
119
}

Ich bin jetzt nicht so der tcl-Crack (siehe Eingangspost). Es kann also 
gut sein, das man da noch was optimieren und verbessern kann.

Duke

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.