HDLRuby 3.9.5 → 3.9.7
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 +182 -9
- data/ext/hruby_sim/hruby_rcsim_build.c +23 -0
- data/ext/hruby_sim/hruby_sim.h +29 -1
- data/ext/hruby_sim/hruby_sim_calc.c +141 -18
- data/ext/hruby_sim/hruby_sim_tree_calc.c +37 -0
- data/lib/HDLRuby/hdr_samples/case_bench.rb +1 -1
- data/lib/HDLRuby/hdr_samples/logic_bench.rb +12 -12
- data/lib/HDLRuby/hdr_samples/with_assign_to_slice.rb +19 -0
- data/lib/HDLRuby/hdr_samples/with_auto_connect.rb +40 -0
- data/lib/HDLRuby/hdr_samples/with_casez.rb +35 -0
- data/lib/HDLRuby/hdr_samples/with_expr_decoder.rb +38 -0
- data/lib/HDLRuby/hdr_samples/with_expr_hif.rb +34 -0
- data/lib/HDLRuby/hdr_samples/with_hbreak.rb +42 -0
- data/lib/HDLRuby/hdr_samples/with_henumerable.rb +1 -1
- data/lib/HDLRuby/hdr_samples/with_values.rb +3 -0
- data/lib/HDLRuby/hruby_high.rb +237 -15
- data/lib/HDLRuby/hruby_low.rb +50 -46
- data/lib/HDLRuby/hruby_rcsim.rb +7 -2
- data/lib/HDLRuby/hruby_verilog.rb +17 -6
- data/lib/HDLRuby/std/decoder.rb +77 -2
- data/lib/HDLRuby/std/hruby_enum.rb +103 -0
- data/lib/HDLRuby/verilog_hruby.rb +30 -0
- data/lib/HDLRuby/verilog_parser.rb +1 -1
- data/lib/HDLRuby/version.rb +1 -1
- metadata +8 -2
|
@@ -1562,10 +1562,14 @@ module HDLRuby::Low
|
|
|
1562
1562
|
# Converts the system to Verilog code.
|
|
1563
1563
|
def to_verilog
|
|
1564
1564
|
# if self.base.name.to_s != "bit"
|
|
1565
|
+
first = self.range.first
|
|
1566
|
+
first = first.to_verilog if first.is_a?(Expression)
|
|
1567
|
+
last = self.range.last
|
|
1568
|
+
last = last.to_verilog if last.is_a?(Expression)
|
|
1565
1569
|
if VERILOG_BASE_TYPES.include?(self.base.name.to_s)
|
|
1566
|
-
|
|
1570
|
+
return " #{self.base.name.to_s}[#{first}:#{last}]"
|
|
1567
1571
|
end
|
|
1568
|
-
return " [#{
|
|
1572
|
+
return " [#{first}:#{last}]"
|
|
1569
1573
|
end
|
|
1570
1574
|
end
|
|
1571
1575
|
|
|
@@ -1630,9 +1634,9 @@ module HDLRuby::Low
|
|
|
1630
1634
|
res = ""
|
|
1631
1635
|
sels = self.select.to_verilog
|
|
1632
1636
|
@choices[0..-2].each_with_index do |choice,i|
|
|
1633
|
-
res << "#{sels} == #{i} ? #{choice.to_verilog} : "
|
|
1637
|
+
res << "(#{sels} == #{i} ? #{choice.to_verilog} : "
|
|
1634
1638
|
end
|
|
1635
|
-
res << @choices[-1].to_verilog
|
|
1639
|
+
res << @choices[-1].to_verilog + (")" * @choices[0..-2].size)
|
|
1636
1640
|
return res
|
|
1637
1641
|
end
|
|
1638
1642
|
end
|
|
@@ -1676,6 +1680,9 @@ module HDLRuby::Low
|
|
|
1676
1680
|
# return "#{self.type.width}'b#{str}"
|
|
1677
1681
|
else
|
|
1678
1682
|
str = self.content.to_verilog
|
|
1683
|
+
if str.length > self.type.width then
|
|
1684
|
+
str = str[str.length-self.type.width..-1]
|
|
1685
|
+
end
|
|
1679
1686
|
if self.content.negative? then
|
|
1680
1687
|
return "#{str.length}'sb#{str}"
|
|
1681
1688
|
else
|
|
@@ -1738,7 +1745,11 @@ module HDLRuby::Low
|
|
|
1738
1745
|
|
|
1739
1746
|
result = " " * spc # Indented based on space_count.
|
|
1740
1747
|
|
|
1741
|
-
|
|
1748
|
+
if @mode == :casez then
|
|
1749
|
+
result << "casez(#{self.value.to_verilog})\n"
|
|
1750
|
+
else
|
|
1751
|
+
result << "case(#{self.value.to_verilog})\n"
|
|
1752
|
+
end
|
|
1742
1753
|
|
|
1743
1754
|
# n the case statement, each branch is partitioned by when. Process each time when.
|
|
1744
1755
|
self.each_when do |whens|
|
|
@@ -2150,7 +2161,7 @@ module HDLRuby::Low
|
|
|
2150
2161
|
inout = self.each_inout.to_a
|
|
2151
2162
|
|
|
2152
2163
|
# Spelling necessary for simulation.
|
|
2153
|
-
code = "`timescale
|
|
2164
|
+
code = "`timescale 10ps/1ps\n\n"
|
|
2154
2165
|
|
|
2155
2166
|
# self.properties[:verilog_name] = vname
|
|
2156
2167
|
# Output the module name.
|
data/lib/HDLRuby/std/decoder.rb
CHANGED
|
@@ -131,10 +131,10 @@ module HDLRuby::High::Std
|
|
|
131
131
|
format = format.to_s
|
|
132
132
|
width = format.size
|
|
133
133
|
# For that purpose create the regular expression used to process it.
|
|
134
|
-
prs = "([0-1]+)|(a+)|(b+)|(c+)|(d+)|(e+)|(g+)|(h+)|(i+)|(j+)|(k+)|(l+)|(m+)|(n+)|(o+)|(p+)|(q+)|(r+)|(s+)|(t+)|(u+)|(v+)|(w+)|(x+)|(y+)|(z+)"
|
|
134
|
+
prs = "([0-1]+)|(a+)|(b+)|(c+)|(d+)|(e+)|(f+)|(g+)|(h+)|(i+)|(j+)|(k+)|(l+)|(m+)|(n+)|(o+)|(p+)|(q+)|(r+)|(s+)|(t+)|(u+)|(v+)|(w+)|(x+)|(y+)|(z+)"
|
|
135
135
|
# Check if the format is compatible with it.
|
|
136
136
|
unless format =~ Regexp.new("^(#{prs})+$") then
|
|
137
|
-
raise AnyError
|
|
137
|
+
raise AnyError, "Invalid format for a field: #{format}"
|
|
138
138
|
end
|
|
139
139
|
# Split the format in fields.
|
|
140
140
|
format = format.split(Regexp.new(prs)).select {|str| !str.empty?}
|
|
@@ -182,6 +182,76 @@ module HDLRuby::High::Std
|
|
|
182
182
|
end
|
|
183
183
|
|
|
184
184
|
|
|
185
|
+
##
|
|
186
|
+
# Class for building an expression decoder.
|
|
187
|
+
#
|
|
188
|
+
class EDecoderMaker
|
|
189
|
+
# Create a new decoder generator for input +sig+ that returns an
|
|
190
|
+
# array of bits for each value from +vals+ that matches given as
|
|
191
|
+
# subsequent arguments.
|
|
192
|
+
# Note: supports chaining using the call operator in wich case
|
|
193
|
+
# [sig,*value] is treated as a composite input signal and each set
|
|
194
|
+
# of values is given by the susequent call.
|
|
195
|
+
def initialize(sig,*values)
|
|
196
|
+
@sig = sig
|
|
197
|
+
@values = values
|
|
198
|
+
if @values.last.is_a?(::Symbol) then
|
|
199
|
+
@op = @values.pop
|
|
200
|
+
else
|
|
201
|
+
@op = :&
|
|
202
|
+
end
|
|
203
|
+
@targets = nil
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
# Add a decoder line in composite case.
|
|
207
|
+
# +target+ is the target bit and +set+ is the of values which sets
|
|
208
|
+
# the target
|
|
209
|
+
def call(target,set)
|
|
210
|
+
if @targets then
|
|
211
|
+
# Already composite, add the values.
|
|
212
|
+
@targets << target
|
|
213
|
+
@values << set
|
|
214
|
+
else
|
|
215
|
+
# Transform the decoder with composite values.
|
|
216
|
+
@sig = [ @sig, *@values ]
|
|
217
|
+
@targets = [ target ]
|
|
218
|
+
@values = [ set ]
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
# Generate the decode line.
|
|
222
|
+
target <= @sig.map.with_index do |sig,i|
|
|
223
|
+
set[i] ? (sig.to_expr == set[i]).as(HDLRuby::High.bit) : HDLRuby::High.top_user._b1
|
|
224
|
+
end.reduce(@op)
|
|
225
|
+
|
|
226
|
+
return self
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
# Convert the expression if to an expression mux.
|
|
230
|
+
def to_expr
|
|
231
|
+
raise "Not an expression decoder." if @targets
|
|
232
|
+
return @values.map do |set|
|
|
233
|
+
(@sig.to_expr == set).as(HDLRuby::High.bit)
|
|
234
|
+
end.to_expr
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
[:"-@",:"+@",:"~", :abs,
|
|
238
|
+
:boolean, :bit, :signed, :unsigned].each do |operator|
|
|
239
|
+
define_method(operator) do
|
|
240
|
+
self.to_expr.send(operator)
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
[:"+",:"-",:"*",:"/",:"%",:"**",
|
|
245
|
+
:"&",:"|",:"^",
|
|
246
|
+
:"<<",:">>",# :ls,:rs,:lr,:rr, # ls, rs lr and rr are treated separately
|
|
247
|
+
:"==",:"!=",:"<",:">",:"<=",:">="].each do |operator|
|
|
248
|
+
define_method(operator) do |expr|
|
|
249
|
+
self.to_expr.send(operator,expr)
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
end
|
|
254
|
+
|
|
185
255
|
|
|
186
256
|
|
|
187
257
|
## Declare a new decoder.
|
|
@@ -192,6 +262,11 @@ module HDLRuby::High::Std
|
|
|
192
262
|
#
|
|
193
263
|
# If provided, +ruby_block+ the fsm is directly instantiated with it.
|
|
194
264
|
def decoder(*args, &ruby_block)
|
|
265
|
+
# Is it an expression decoder?
|
|
266
|
+
unless ruby_block then
|
|
267
|
+
# Yes.
|
|
268
|
+
return EDecoderMaker.new(*args)
|
|
269
|
+
end
|
|
195
270
|
# Sets the name if any
|
|
196
271
|
unless args[0].respond_to?(:to_expr) then
|
|
197
272
|
name = args.shift.to_sym
|
|
@@ -1261,4 +1261,107 @@ module HDLRuby::High::Std
|
|
|
1261
1261
|
end
|
|
1262
1262
|
end
|
|
1263
1263
|
|
|
1264
|
+
module BlockHBreak
|
|
1265
|
+
# Build the block by executing +ruby_block+.
|
|
1266
|
+
def build(&ruby_block)
|
|
1267
|
+
High.space_push(@namespace)
|
|
1268
|
+
cur_hbreak = true
|
|
1269
|
+
catch(:HDLRubyThrow) do
|
|
1270
|
+
@return_value = High.top_user.instance_eval(&ruby_block)
|
|
1271
|
+
cur_hbreak = false
|
|
1272
|
+
end
|
|
1273
|
+
@hbreak = true if cur_hbreak
|
|
1274
|
+
High.space_pop
|
|
1275
|
+
@return_value
|
|
1276
|
+
end
|
|
1277
|
+
|
|
1278
|
+
# # Creates and adds a new block executed in +mode+, with possible
|
|
1279
|
+
# # +name+ and built by executing +ruby_block+.
|
|
1280
|
+
# def add_block(mode = nil, name = :"", &ruby_block)
|
|
1281
|
+
# # Creates the block.
|
|
1282
|
+
# block = High.make_block(mode,name,&ruby_block)
|
|
1283
|
+
# # Adds it as a statement.
|
|
1284
|
+
# self.add_statement(block)
|
|
1285
|
+
# # Handle the breaks if any.
|
|
1286
|
+
# @hbreak = block.hbreak
|
|
1287
|
+
# # Use its return value.
|
|
1288
|
+
# return block.return_value
|
|
1289
|
+
# end
|
|
1290
|
+
|
|
1291
|
+
# Adds a +statement+.
|
|
1292
|
+
#
|
|
1293
|
+
# NOTE: TimeWait is not supported unless for TimeBlock objects.
|
|
1294
|
+
def add_statement(statement)
|
|
1295
|
+
res = super(statement)
|
|
1296
|
+
hbrk = statement.instance_variable_get(:@hbreak)
|
|
1297
|
+
@hbreak = hbrk if hbrk
|
|
1298
|
+
return res
|
|
1299
|
+
end
|
|
1300
|
+
end
|
|
1301
|
+
|
|
1302
|
+
HDLRuby::High::Block.prepend(BlockHBreak)
|
|
1303
|
+
|
|
1304
|
+
module IfHBreak
|
|
1305
|
+
# Creates a new if statement with a +condition+ that when met lead
|
|
1306
|
+
# to the execution of the block in +mode+ generated by the execution of
|
|
1307
|
+
# +ruby_block+.
|
|
1308
|
+
def initialize(condition, mode = nil, &ruby_block)
|
|
1309
|
+
super
|
|
1310
|
+
hbrk = yes.instance_variable_get(:@hbreak)
|
|
1311
|
+
@hbreak = hbrk if hbrk
|
|
1312
|
+
end
|
|
1313
|
+
|
|
1314
|
+
# Sets the no block.
|
|
1315
|
+
#
|
|
1316
|
+
# No shoud only be set once, but this is not checked here for
|
|
1317
|
+
# sake of flexibility.
|
|
1318
|
+
def no=(no)
|
|
1319
|
+
res = super(no)
|
|
1320
|
+
hbrk = no.instance_variable_get(:@hbreak)
|
|
1321
|
+
raise AnyError, "Invalid hbreak." if hbrk
|
|
1322
|
+
return res
|
|
1323
|
+
end
|
|
1324
|
+
|
|
1325
|
+
# Adds an alternative if statement (elsif) testing +next_cond+
|
|
1326
|
+
# and executing +next_yes+ when the condition is met.
|
|
1327
|
+
def add_noif(next_cond, next_yes)
|
|
1328
|
+
res = super(next_cond,next_yes)
|
|
1329
|
+
hbrk = next_yes.instance_variable_get(:@hbreak)
|
|
1330
|
+
raise AnyError, "Invalid hbreak." if hbrk
|
|
1331
|
+
return res
|
|
1332
|
+
end
|
|
1333
|
+
end
|
|
1334
|
+
|
|
1335
|
+
HDLRuby::High::If.prepend(IfHBreak)
|
|
1336
|
+
|
|
1337
|
+
module BehaviorHBreak
|
|
1338
|
+
# Creates a new behavior executing +block+ activated on a list of
|
|
1339
|
+
# +events+, possible name (of main block) +name+ and built by
|
|
1340
|
+
# executing +ruby_block+.
|
|
1341
|
+
# +mode+ can be either :seq or :par for respectively sequential or
|
|
1342
|
+
# parallel.
|
|
1343
|
+
def initialize(mode, *events, name: nil, &ruby_block)
|
|
1344
|
+
super(mode,*events, name: name, &ruby_block)
|
|
1345
|
+
if self.block.instance_variable_get(:@hbreak) then
|
|
1346
|
+
stmnt0 = self.block.each_statement.first
|
|
1347
|
+
unless stmnt0.is_a?(HDLRuby::High::If) then
|
|
1348
|
+
raise AnyError, "Unsuported usage of hbreak."
|
|
1349
|
+
end
|
|
1350
|
+
if stmnt0.no || stmnt0.each_noif.any? then
|
|
1351
|
+
raise AnyError, "Unsuported usage of hbreak."
|
|
1352
|
+
end
|
|
1353
|
+
# Extract the hif an put it as the single statement whose else
|
|
1354
|
+
# part is the current block.
|
|
1355
|
+
self.block.delete_statement!(stmnt0)
|
|
1356
|
+
self.block.parent = nil
|
|
1357
|
+
self.block.instance_variable_set(:@hbreak,false)
|
|
1358
|
+
stmnt0.no = self.block
|
|
1359
|
+
self.block = HDLRuby::High.make_block(self.block.mode) {}
|
|
1360
|
+
self.block.add_statement(stmnt0)
|
|
1361
|
+
end
|
|
1362
|
+
end
|
|
1363
|
+
end
|
|
1364
|
+
|
|
1365
|
+
HDLRuby::High::Behavior.prepend(BehaviorHBreak)
|
|
1366
|
+
|
|
1264
1367
|
end
|
|
@@ -889,6 +889,34 @@ module VerilogTools
|
|
|
889
889
|
state.indent = indent
|
|
890
890
|
state.level = level
|
|
891
891
|
return case_txt
|
|
892
|
+
when :casez
|
|
893
|
+
# Saves the state.
|
|
894
|
+
indent = state.indent
|
|
895
|
+
level = state.level
|
|
896
|
+
# Generate the hcase.
|
|
897
|
+
state.indent += " "
|
|
898
|
+
case_txt = indent + "hcasez(" + ast[1].to_HDLRuby(state) + ")\n"
|
|
899
|
+
# Generate the case items.
|
|
900
|
+
case_txt += ast[2].map do |item|
|
|
901
|
+
res_txt = ""
|
|
902
|
+
if item[0] != "default" then
|
|
903
|
+
# hwhen case.
|
|
904
|
+
res_txt += indent + "hwhen("
|
|
905
|
+
res_txt += item[0].map {|e| e.to_HDLRuby(state) }.join(",")
|
|
906
|
+
res_txt += ") do\n"
|
|
907
|
+
res_txt += item[1].to_HDLRuby(state)
|
|
908
|
+
res_txt += indent + "end\n"
|
|
909
|
+
else
|
|
910
|
+
# helse case.
|
|
911
|
+
res_txt += indent + "helse do\n"
|
|
912
|
+
res_txt += item[1].to_HDLRuby(state) + indent + "end\n"
|
|
913
|
+
end
|
|
914
|
+
res_txt
|
|
915
|
+
end.join
|
|
916
|
+
# Restore the state and return the result.
|
|
917
|
+
state.indent = indent
|
|
918
|
+
state.level = level
|
|
919
|
+
return case_txt
|
|
892
920
|
when :blocking_assignment, :non_blocking_assignment
|
|
893
921
|
return ast[0].to_HDLRuby(state)
|
|
894
922
|
when :statement
|
|
@@ -1094,6 +1122,8 @@ module VerilogTools
|
|
|
1094
1122
|
base = ast[1] ? ast[1].to_HDLRuby(state) : ""
|
|
1095
1123
|
# Get the second number if any.
|
|
1096
1124
|
number1 = ast[2] ? ast[2].to_HDLRuby(state) : ""
|
|
1125
|
+
# In HDLRuby, for now, wildcards work like z.
|
|
1126
|
+
number1 = number1.gsub("?","z")
|
|
1097
1127
|
# Depending on the base.
|
|
1098
1128
|
case base
|
|
1099
1129
|
when "'b"
|
|
@@ -1220,7 +1220,7 @@ module VerilogTools
|
|
|
1220
1220
|
CLOSE_BRA_COLON_TOKS = [ "\\" + CLOSE_BRA_TOK, COLON_TOK ]
|
|
1221
1221
|
CLOSE_BRA_COLON_REX = /\G#{S}(#{CLOSE_BRA_COLON_TOKS.join("|")})/
|
|
1222
1222
|
|
|
1223
|
-
STATEMENT_TOKS = [ IF_TOK,
|
|
1223
|
+
STATEMENT_TOKS = [ IF_TOK, CASEZ_TOK, CASEX_TOK, CASE_TOK,
|
|
1224
1224
|
FOREVER_TOK, REPEAT_TOK, WHILE_TOK, FOR_TOK,
|
|
1225
1225
|
WAIT_TOK, RIGHT_ARROW_TOK, DISABLE_TOK,
|
|
1226
1226
|
ASSIGN_TOK, FORCE_TOK, DEASSIGN_TOK, RELEASE_TOK ]
|
data/lib/HDLRuby/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: HDLRuby
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.9.
|
|
4
|
+
version: 3.9.7
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Lovic Gauthier
|
|
@@ -193,11 +193,14 @@ files:
|
|
|
193
193
|
- lib/HDLRuby/hdr_samples/tuple.rb
|
|
194
194
|
- lib/HDLRuby/hdr_samples/type_minmax_bench.rb
|
|
195
195
|
- lib/HDLRuby/hdr_samples/verilog_parser_bench.rb
|
|
196
|
+
- lib/HDLRuby/hdr_samples/with_assign_to_slice.rb
|
|
197
|
+
- lib/HDLRuby/hdr_samples/with_auto_connect.rb
|
|
196
198
|
- lib/HDLRuby/hdr_samples/with_board.rb
|
|
197
199
|
- lib/HDLRuby/hdr_samples/with_board_sequencer.rb
|
|
198
200
|
- lib/HDLRuby/hdr_samples/with_bram.rb
|
|
199
201
|
- lib/HDLRuby/hdr_samples/with_bram_frame_stack.rb
|
|
200
202
|
- lib/HDLRuby/hdr_samples/with_bram_stack.rb
|
|
203
|
+
- lib/HDLRuby/hdr_samples/with_casez.rb
|
|
201
204
|
- lib/HDLRuby/hdr_samples/with_casts.rb
|
|
202
205
|
- lib/HDLRuby/hdr_samples/with_channel.rb
|
|
203
206
|
- lib/HDLRuby/hdr_samples/with_channel_other.rb
|
|
@@ -210,12 +213,15 @@ files:
|
|
|
210
213
|
- lib/HDLRuby/hdr_samples/with_def.rb
|
|
211
214
|
- lib/HDLRuby/hdr_samples/with_define_operator.rb
|
|
212
215
|
- lib/HDLRuby/hdr_samples/with_delay.rb
|
|
216
|
+
- lib/HDLRuby/hdr_samples/with_expr_decoder.rb
|
|
217
|
+
- lib/HDLRuby/hdr_samples/with_expr_hif.rb
|
|
213
218
|
- lib/HDLRuby/hdr_samples/with_fixpoint.rb
|
|
214
219
|
- lib/HDLRuby/hdr_samples/with_fixpoint_adv.rb
|
|
215
220
|
- lib/HDLRuby/hdr_samples/with_fsm.rb
|
|
216
221
|
- lib/HDLRuby/hdr_samples/with_function_generator.rb
|
|
217
222
|
- lib/HDLRuby/hdr_samples/with_generic_in_generic.rb
|
|
218
223
|
- lib/HDLRuby/hdr_samples/with_handshake.rb
|
|
224
|
+
- lib/HDLRuby/hdr_samples/with_hbreak.rb
|
|
219
225
|
- lib/HDLRuby/hdr_samples/with_henumerable.rb
|
|
220
226
|
- lib/HDLRuby/hdr_samples/with_init.rb
|
|
221
227
|
- lib/HDLRuby/hdr_samples/with_instance.rb
|
|
@@ -502,7 +508,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
502
508
|
- !ruby/object:Gem::Version
|
|
503
509
|
version: '0'
|
|
504
510
|
requirements: []
|
|
505
|
-
rubygems_version: 4.0.
|
|
511
|
+
rubygems_version: 4.0.20
|
|
506
512
|
specification_version: 4
|
|
507
513
|
summary: HDLRuby is a library for describing and simulating digital electronic systems.
|
|
508
514
|
test_files: []
|