HDLRuby 2.3.8 → 2.4.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +1 -0
- data/lib/HDLRuby/hdr_samples/bstr_bench.rb +14 -0
- data/lib/HDLRuby/hdr_samples/neg_arith_bench.rb +49 -0
- data/lib/HDLRuby/hdr_samples/with_fixpoint.rb +18 -0
- data/lib/HDLRuby/hdr_samples/with_memory_rom.rb +53 -0
- data/lib/HDLRuby/hdr_samples/with_multi_channels.rb +300 -0
- data/lib/HDLRuby/hdrcc.rb +10 -1
- data/lib/HDLRuby/hruby_bstr.rb +7 -2
- data/lib/HDLRuby/hruby_high.rb +8 -0
- data/lib/HDLRuby/hruby_low.rb +17 -11
- data/lib/HDLRuby/hruby_low2c.rb +31 -9
- data/lib/HDLRuby/hruby_low_resolve.rb +1 -0
- data/lib/HDLRuby/hruby_low_without_connection.rb +1 -0
- data/lib/HDLRuby/sim/hruby_sim.h +82 -39
- data/lib/HDLRuby/sim/hruby_sim_calc.c +89 -5
- data/lib/HDLRuby/sim/hruby_sim_core.c +32 -6
- data/lib/HDLRuby/sim/hruby_sim_vcd.c +380 -0
- data/lib/HDLRuby/sim/hruby_sim_vizualize.c +51 -12
- data/lib/HDLRuby/std/fixpoint.rb +10 -2
- data/lib/HDLRuby/std/linear.rb +19 -7
- data/lib/HDLRuby/std/memory.rb +178 -29
- data/lib/HDLRuby/version.rb +1 -1
- metadata +7 -2
|
@@ -12,6 +12,9 @@
|
|
|
12
12
|
* hruby_low2c.
|
|
13
13
|
* */
|
|
14
14
|
|
|
15
|
+
/** The top system. */
|
|
16
|
+
SystemT top_system;
|
|
17
|
+
|
|
15
18
|
/** The number of all the signals. */
|
|
16
19
|
static int num_all_signals = 0;
|
|
17
20
|
/** The capacity of the set of signals. */
|
|
@@ -118,9 +121,10 @@ void hruby_sim_update_signals() {
|
|
|
118
121
|
/* Is there a change? */
|
|
119
122
|
if (same_content_value(sig->c_value,sig->f_value)) continue;
|
|
120
123
|
/* Yes, process the signal. */
|
|
121
|
-
println_signal(sig);
|
|
122
|
-
|
|
123
|
-
// printf("
|
|
124
|
+
// println_signal(sig);
|
|
125
|
+
printer.print_signal(sig);
|
|
126
|
+
// printf("c_value="); printer.print_value(sig->c_value);
|
|
127
|
+
// printf("\nf_value="); printer.print_value(sig->f_value); printf("\n");
|
|
124
128
|
// printf("Touched signal: %p (%s)\n",sig,sig->name);
|
|
125
129
|
/* Update the current value of the signal. */
|
|
126
130
|
copy_value(sig->f_value,sig->c_value);
|
|
@@ -185,7 +189,8 @@ void hruby_sim_update_signals() {
|
|
|
185
189
|
SignalI sig = e->data;
|
|
186
190
|
delete_element(e);
|
|
187
191
|
/* Yes, process the signal. */
|
|
188
|
-
println_signal(sig);
|
|
192
|
+
// println_signal(sig);
|
|
193
|
+
printer.print_signal(sig);
|
|
189
194
|
/* Update the current value of the signal. */
|
|
190
195
|
/* Mark the corresponding code as activated. */
|
|
191
196
|
/* Any edge activation. */
|
|
@@ -284,7 +289,8 @@ void hruby_sim_advance_time() {
|
|
|
284
289
|
}
|
|
285
290
|
/* Sets the new activation time. */
|
|
286
291
|
hruby_sim_time = next_time;
|
|
287
|
-
println_time(hruby_sim_time);
|
|
292
|
+
// println_time(hruby_sim_time);
|
|
293
|
+
printer.print_time(hruby_sim_time);
|
|
288
294
|
}
|
|
289
295
|
|
|
290
296
|
|
|
@@ -393,9 +399,18 @@ void hruby_sim_end_timed_behaviors() {
|
|
|
393
399
|
|
|
394
400
|
|
|
395
401
|
|
|
402
|
+
// /** The simulation core function.
|
|
403
|
+
// * @param limit the time limit in fs. */
|
|
404
|
+
// void hruby_sim_core(unsigned long long limit) {
|
|
396
405
|
/** The simulation core function.
|
|
406
|
+
* @param name the name of the simulation.
|
|
407
|
+
* @param vizualizer the vizualizer engine initializer.
|
|
397
408
|
* @param limit the time limit in fs. */
|
|
398
|
-
void hruby_sim_core(
|
|
409
|
+
void hruby_sim_core(char* name, void (*init_vizualizer)(char*),
|
|
410
|
+
unsigned long long limit) {
|
|
411
|
+
/* Initilize the vizualizer. */
|
|
412
|
+
init_vizualizer(name);
|
|
413
|
+
|
|
399
414
|
/* Initialize the time to 0. */
|
|
400
415
|
hruby_sim_time = 0;
|
|
401
416
|
|
|
@@ -597,3 +612,14 @@ unsigned long long make_delay(int value, Unit unit) {
|
|
|
597
612
|
return -1;
|
|
598
613
|
}
|
|
599
614
|
|
|
615
|
+
|
|
616
|
+
|
|
617
|
+
|
|
618
|
+
/* Iterate over all the signals.
|
|
619
|
+
* @param func function to applie on each signal. */
|
|
620
|
+
void each_all_signal(void (*func)(SignalI)) {
|
|
621
|
+
int i;
|
|
622
|
+
for(i = 0; i<num_all_signals; ++i) {
|
|
623
|
+
func(all_signals[i]);
|
|
624
|
+
}
|
|
625
|
+
}
|
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
#include <stdio.h>
|
|
2
|
+
#include <string.h>
|
|
3
|
+
#include <time.h>
|
|
4
|
+
#include <stdarg.h>
|
|
5
|
+
#include "hruby_sim.h"
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* The HDLRuby simulation vcd format genertion engine, to be used with C code
|
|
10
|
+
* generated by hruby_low2c.
|
|
11
|
+
**/
|
|
12
|
+
|
|
13
|
+
/* Global variables storing the configuration of the vcd generation. */
|
|
14
|
+
|
|
15
|
+
/* The target file. */
|
|
16
|
+
static FILE* vcd_file;
|
|
17
|
+
|
|
18
|
+
/* The time scale unit in the ps. */
|
|
19
|
+
static unsigned long long vcd_timeunit = 1;
|
|
20
|
+
|
|
21
|
+
/* Accessing target file. */
|
|
22
|
+
|
|
23
|
+
/** Prints to the vcd file.
|
|
24
|
+
* @param fmt the format for handling the variadic arguments. */
|
|
25
|
+
static int vcd_print(char* fmt, ...) {
|
|
26
|
+
int ret;
|
|
27
|
+
|
|
28
|
+
/* Declare a va_list type variable */
|
|
29
|
+
va_list myargs;
|
|
30
|
+
|
|
31
|
+
/* Initialise the va_list variable with the ... after fmt */
|
|
32
|
+
va_start(myargs, fmt);
|
|
33
|
+
|
|
34
|
+
/* Forward the '...' to vprintf */
|
|
35
|
+
ret = vfprintf(vcd_file, fmt, myargs);
|
|
36
|
+
|
|
37
|
+
/* Clean up the va_list */
|
|
38
|
+
va_end(myargs);
|
|
39
|
+
|
|
40
|
+
return ret;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
/* Utility functions for printing in vcd format. */
|
|
45
|
+
|
|
46
|
+
/* Replace all the occurences of a character with another.
|
|
47
|
+
* CREDIT: Superlokkus */
|
|
48
|
+
static char* replace_char(char* str, char find, char replace){
|
|
49
|
+
char *current_pos = strchr(str,find);
|
|
50
|
+
while (current_pos){
|
|
51
|
+
*current_pos = replace;
|
|
52
|
+
current_pos = strchr(current_pos+1,find);
|
|
53
|
+
}
|
|
54
|
+
return str;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
/* Low-end print functions. */
|
|
59
|
+
|
|
60
|
+
/** Prints the time.
|
|
61
|
+
* @param time the time to show (given in ps). */
|
|
62
|
+
static void vcd_print_time(unsigned long long time) {
|
|
63
|
+
vcd_print("#%llu\n",time/vcd_timeunit);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
/** Prints the name of an object without its hierarchy.
|
|
68
|
+
* @param object the object to print the name. */
|
|
69
|
+
static void vcd_print_name(Object object) {
|
|
70
|
+
/* Depending on the kind of object. */
|
|
71
|
+
switch(object->kind) {
|
|
72
|
+
case SYSTEMT:
|
|
73
|
+
case SIGNALI:
|
|
74
|
+
case SCOPE:
|
|
75
|
+
case SYSTEMI:
|
|
76
|
+
case BLOCK:
|
|
77
|
+
/* Print the name if name. */
|
|
78
|
+
/* Trick: SystemT, SignalI, Scope and SystemI have the
|
|
79
|
+
* field name at the same place. */
|
|
80
|
+
if ((((Block)object)->name != NULL) &&
|
|
81
|
+
strlen(((Block)object)->name)>0) {
|
|
82
|
+
char name[256];
|
|
83
|
+
strncpy(name,((Block)object)->name,256);
|
|
84
|
+
replace_char(name,':','$');
|
|
85
|
+
vcd_print("%s",name);
|
|
86
|
+
} else {
|
|
87
|
+
/* No name, use the address of the object as name generator.*/
|
|
88
|
+
vcd_print("$%p",(void*)object);
|
|
89
|
+
}
|
|
90
|
+
break;
|
|
91
|
+
default: /* Nothing to do */
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
/** Prints the name of an object incluing its heirarchy.
|
|
98
|
+
* @param object the object to print the name. */
|
|
99
|
+
static void vcd_print_full_name(Object object) {
|
|
100
|
+
/* Recurse on the owner if any. */
|
|
101
|
+
// printf("owner=%p\n",object->owner);
|
|
102
|
+
if (object->owner != NULL) {
|
|
103
|
+
vcd_print_full_name(object->owner);
|
|
104
|
+
vcd_print("$");
|
|
105
|
+
}
|
|
106
|
+
/* Print the name of the object. */
|
|
107
|
+
vcd_print_name(object);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** Prints a value.
|
|
111
|
+
* @param value the value to print */
|
|
112
|
+
static void vcd_print_value(Value value) {
|
|
113
|
+
vcd_print("b");
|
|
114
|
+
if (value->numeric) {
|
|
115
|
+
unsigned long long width = type_width(value->type);
|
|
116
|
+
unsigned long long mask = 1ULL << (width-1);
|
|
117
|
+
for(; mask > 0; mask >>= 1) {
|
|
118
|
+
vcd_print("%d",(value->data_int & mask) != 0);
|
|
119
|
+
}
|
|
120
|
+
} else {
|
|
121
|
+
/* Display a bitstring value. */
|
|
122
|
+
int i;
|
|
123
|
+
int width = type_width(value->type);
|
|
124
|
+
char* data = value->data_str;
|
|
125
|
+
if (value->capacity == 0) {
|
|
126
|
+
/* The value is empty, therefore undefined. */
|
|
127
|
+
for(i=width; i>0; --i) {
|
|
128
|
+
vcd_print("u");
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
/* The value is not empty. */
|
|
133
|
+
for(i=width; i>0; --i) {
|
|
134
|
+
vcd_print("%c",data[i-1]);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/** Prints a signal declaration.
|
|
141
|
+
* @param signal the signal to declare */
|
|
142
|
+
static void vcd_print_var(SignalI signal) {
|
|
143
|
+
vcd_print("$var wire %d ",type_width(signal->type));
|
|
144
|
+
vcd_print_full_name((Object)signal);
|
|
145
|
+
vcd_print(" ");
|
|
146
|
+
vcd_print_name((Object)signal);
|
|
147
|
+
vcd_print(" $end\n");
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
/** Prints a signal with its future value if any.
|
|
152
|
+
* @param signal the signal to show */
|
|
153
|
+
static void vcd_print_signal_fvalue(SignalI signal) {
|
|
154
|
+
if (signal->f_value) {
|
|
155
|
+
vcd_print_value(signal->f_value);
|
|
156
|
+
vcd_print(" ");
|
|
157
|
+
vcd_print_full_name((Object)signal);
|
|
158
|
+
vcd_print("\n");
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
/** Prints a signal with its current value if any
|
|
164
|
+
* @param signal the signal to show */
|
|
165
|
+
static void vcd_print_signal_cvalue(SignalI signal) {
|
|
166
|
+
if (signal->c_value) {
|
|
167
|
+
vcd_print_value(signal->c_value);
|
|
168
|
+
vcd_print(" ");
|
|
169
|
+
vcd_print_full_name((Object)signal);
|
|
170
|
+
vcd_print("\n");
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
/** Prints the hierarchy content of a system type.
|
|
176
|
+
* @param system the system to print. */
|
|
177
|
+
static void vcd_print_systemT_content(SystemT system);
|
|
178
|
+
|
|
179
|
+
/** Prints the hierarchy of a scope.
|
|
180
|
+
* @param scope the scope to print. */
|
|
181
|
+
static void vcd_print_scope(Scope scope);
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
/** Prints the hierarchy of a block.
|
|
185
|
+
* @param block the block to print. */
|
|
186
|
+
static void vcd_print_block(Block block) {
|
|
187
|
+
int i;
|
|
188
|
+
/* Do not print block with no declaration. */
|
|
189
|
+
if (block->num_inners == 0) return;
|
|
190
|
+
|
|
191
|
+
/* Declares the block if named. */
|
|
192
|
+
vcd_print("$scope module ");
|
|
193
|
+
vcd_print_name((Object)block);
|
|
194
|
+
vcd_print(" $end\n");
|
|
195
|
+
|
|
196
|
+
/* Declare the inners of the systems. */
|
|
197
|
+
for(i=0; i<block->num_inners; ++i) {
|
|
198
|
+
vcd_print_var(block->inners[i]);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/* Close the hierarchy. */
|
|
202
|
+
vcd_print("$upscope $end\n");
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
/** Prints the hierarchy of a system instances.
|
|
207
|
+
* @param scope the scope to print. */
|
|
208
|
+
static void vcd_print_systemI(SystemI systemI) {
|
|
209
|
+
/* Declares the systemI. */
|
|
210
|
+
vcd_print("$scope module ");
|
|
211
|
+
vcd_print_name((Object)systemI);
|
|
212
|
+
vcd_print(" $end\n");
|
|
213
|
+
|
|
214
|
+
/* Declares its content. */
|
|
215
|
+
vcd_print_systemT_content(systemI->system);
|
|
216
|
+
|
|
217
|
+
/* Close the hierarchy. */
|
|
218
|
+
vcd_print("$upscope $end\n");
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
/** Prints the hierarchy inside a scope.
|
|
223
|
+
* @param scope the scope to print the inside. */
|
|
224
|
+
static void vcd_print_scope_content(Scope scope) {
|
|
225
|
+
int i;
|
|
226
|
+
|
|
227
|
+
/* Declare the inners of the systems. */
|
|
228
|
+
for(i=0; i<scope->num_inners; ++i) {
|
|
229
|
+
vcd_print_var(scope->inners[i]);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/* Recurse on the system instances. */
|
|
233
|
+
for(i=0; i<scope->num_systemIs; ++i) {
|
|
234
|
+
vcd_print_systemI(scope->systemIs[i]);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/* Recurse on the sub scopes. */
|
|
238
|
+
for(i=0; i<scope->num_scopes; ++i) {
|
|
239
|
+
vcd_print_scope(scope->scopes[i]);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/* Recurse on the behaviors. */
|
|
243
|
+
for(i=0; i<scope->num_behaviors; ++i) {
|
|
244
|
+
vcd_print_block(scope->behaviors[i]->block);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
/** Prints the hierarchy of a scope.
|
|
250
|
+
* @param scope the scope to print. */
|
|
251
|
+
static void vcd_print_scope(Scope scope) {
|
|
252
|
+
/* Do not print block with no declaration. */
|
|
253
|
+
if (scope->num_inners == 0 && scope->num_scopes == 0 && scope->num_behaviors == 0) return;
|
|
254
|
+
/* Declares the scope. */
|
|
255
|
+
vcd_print("$scope module ");
|
|
256
|
+
vcd_print_name((Object)scope);
|
|
257
|
+
vcd_print(" $end\n");
|
|
258
|
+
|
|
259
|
+
/* Declares its content. */
|
|
260
|
+
vcd_print_scope_content(scope);
|
|
261
|
+
|
|
262
|
+
/* Close the hierarchy. */
|
|
263
|
+
vcd_print("$upscope $end\n");
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
/** Prints the hierarchy content of a system type.
|
|
268
|
+
* @param system the system to print. */
|
|
269
|
+
static void vcd_print_systemT_content(SystemT system) {
|
|
270
|
+
int i;
|
|
271
|
+
|
|
272
|
+
/* Declare the inputs of the systems. */
|
|
273
|
+
for(i = 0; i<system->num_inputs; ++i) {
|
|
274
|
+
vcd_print_var(system->inputs[i]);
|
|
275
|
+
}
|
|
276
|
+
/* Declare the outputs of the systems. */
|
|
277
|
+
for(i = 0; i<system->num_outputs; ++i) {
|
|
278
|
+
vcd_print_var(system->outputs[i]);
|
|
279
|
+
}
|
|
280
|
+
/* Declare the inouts of the systems. */
|
|
281
|
+
for(i = 0; i<system->num_inouts; ++i) {
|
|
282
|
+
vcd_print_var(system->inouts[i]);
|
|
283
|
+
}
|
|
284
|
+
/* Recurse on the content of the scope (the scope header is the system).*/
|
|
285
|
+
vcd_print_scope_content(system->scope);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
/** Prints the hierarchy of a system type.
|
|
290
|
+
* @param system the system to print. */
|
|
291
|
+
static void vcd_print_systemT(SystemT system) {
|
|
292
|
+
int i;
|
|
293
|
+
/* Declares the module. */
|
|
294
|
+
vcd_print("$scope module ");
|
|
295
|
+
vcd_print_name((Object)system);
|
|
296
|
+
vcd_print(" $end\n");
|
|
297
|
+
|
|
298
|
+
/* Declares the content. */
|
|
299
|
+
vcd_print_systemT_content(system);
|
|
300
|
+
|
|
301
|
+
/* Close the hierarchy. */
|
|
302
|
+
vcd_print("$upscope $end\n");
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
/* high-end print functions. */
|
|
310
|
+
|
|
311
|
+
/** Prints the header of the vcd file. */
|
|
312
|
+
static void vcd_print_header() {
|
|
313
|
+
/* The date section. */
|
|
314
|
+
vcd_print("$date\n");
|
|
315
|
+
{
|
|
316
|
+
char text[100];
|
|
317
|
+
time_t now = time(NULL);
|
|
318
|
+
struct tm *t = localtime(&now);
|
|
319
|
+
strftime(text, sizeof(text), "%d %m %Y %H:%M", t);
|
|
320
|
+
vcd_print(" %s\n", text);
|
|
321
|
+
}
|
|
322
|
+
vcd_print("$end\n");
|
|
323
|
+
|
|
324
|
+
/* The version section. */
|
|
325
|
+
vcd_print("$version\n");
|
|
326
|
+
vcd_print(" Generated from HDLRuby simulator\n");
|
|
327
|
+
vcd_print("$end\n");
|
|
328
|
+
|
|
329
|
+
/* The comment section. */
|
|
330
|
+
vcd_print("$comment\n");
|
|
331
|
+
vcd_print(" Is it an easter egg?\n");
|
|
332
|
+
vcd_print("$end\n");
|
|
333
|
+
|
|
334
|
+
/* The time scale section: for now 1ps only. */
|
|
335
|
+
vcd_print("$timescale 1ps $end\n");
|
|
336
|
+
|
|
337
|
+
// /* The scope section: nothing specific. */
|
|
338
|
+
// vcd_print("$scope module logic $end\n");
|
|
339
|
+
|
|
340
|
+
// /* The variables declaration. */
|
|
341
|
+
// each_all_signal(&vcd_print_var);
|
|
342
|
+
|
|
343
|
+
// /* Ends the declarations. */
|
|
344
|
+
// vcd_print("$upscope $end\n");
|
|
345
|
+
// vcd_print("$enddefinitions $end\n");
|
|
346
|
+
|
|
347
|
+
/* The declaration of the hierarchy and the variables
|
|
348
|
+
* from the top system. */
|
|
349
|
+
vcd_print_systemT(top_system);
|
|
350
|
+
/* Ends the declarations. */
|
|
351
|
+
vcd_print("$enddefinitions $end\n");
|
|
352
|
+
|
|
353
|
+
/* Display the initializations. */
|
|
354
|
+
vcd_print("$dumpvars\n");
|
|
355
|
+
each_all_signal(&vcd_print_signal_cvalue);
|
|
356
|
+
vcd_print("$end\n");
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
/* The configuration and initialization of the vcd vizualizer. */
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
/** Sets up the vcd vizualization engine.
|
|
364
|
+
* @param name the name of the vizualization. */
|
|
365
|
+
extern void init_vcd_visualizer(char* name) {
|
|
366
|
+
/* Open the resulting file with name: <name>.vcd */
|
|
367
|
+
char filename[256];
|
|
368
|
+
strncpy(filename,name,256);
|
|
369
|
+
strncat(filename,".vcd",256);
|
|
370
|
+
vcd_file = fopen(filename,"w");
|
|
371
|
+
|
|
372
|
+
/* Initialize the vizualizer printer engine. */
|
|
373
|
+
init_visualizer(&vcd_print_time,
|
|
374
|
+
&vcd_print_full_name,
|
|
375
|
+
&vcd_print_value,
|
|
376
|
+
&vcd_print_signal_fvalue);
|
|
377
|
+
|
|
378
|
+
/* Prints the header of the vcd file. */
|
|
379
|
+
vcd_print_header();
|
|
380
|
+
}
|