[topgen/tlgen] Move simplify_addr to tlgen
Previously, topgen simplified the address list of Xbar device port by
merging, compacting the multiple addresses into one.
The logic is self-contained, not affected by other crossbars. So it is
more logical that the tlgen has the optimization not in the topgen.
This commit is to refactor the code by moving the functions into tlgen.
Still it would be better that the optimization is in the elaboration
step not in the validation step. It was easier to move to validation as
the simplify function uses Hjson object not the Xbar instances.
Later, the logic can be tweaked by using Xbar instance (Node instances)
and do the optimization after validation step.
This closes #1302
Signed-off-by: Eunchan Kim <eunchan@opentitan.org>
diff --git a/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson b/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson
index 0d4913b..272d144 100644
--- a/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson
+++ b/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson
@@ -746,7 +746,15 @@
[
{
base_addr: 0x40000000
- size_byte: 0x21000
+ size_byte: 0x1000
+ }
+ {
+ base_addr: 0x40010000
+ size_byte: 0x1000
+ }
+ {
+ base_addr: 0x40020000
+ size_byte: 0x1000
}
{
base_addr: 0x40080000
diff --git a/util/tlgen/lib.py b/util/tlgen/lib.py
index 528de26..a5333ff 100644
--- a/util/tlgen/lib.py
+++ b/util/tlgen/lib.py
@@ -19,3 +19,168 @@
t = t * 2
return False
+
+
+def simplify_addr(dev, xbar):
+ """Make smaller entries by combining them
+
+ If any contiguous regions exist, concatenate them.
+ For instance, 0x1000 ~ 0x1FFF , 0x2000~ 0x2FFF ==> 0x1000 ~ 0x2FFF
+
+ It also checks if there's no device between the gap, then merge
+ the ranges. For instance:
+
+ {0x4000_0000, 0x1_0000}, {0x4008_0000, 0x1_0000} then it combines two
+ entries into {0x4000_0000, 0x9_0000}
+
+ @param addrs List of Dict[Addr] : {'base_addr':,'size_byte':}
+ """
+
+ addrs = dev["addr_range"]
+ # Sort based on the base addr
+ newlist = sorted(addrs, key=lambda k: int(k['base_addr'], 0))
+ # check if overlap or contiguous
+ result = []
+ for e in newlist:
+ if len(result) == 0:
+ result.append(e)
+ continue
+ # if contiguous
+ if int(e["base_addr"], 0) == int(result[-1]["base_addr"], 0) + int(
+ result[-1]["size_byte"], 0):
+ # update previous entry size
+ result[-1]["size_byte"] = "0x{:x}".format(
+ int(result[-1]["size_byte"], 0) + int(e["size_byte"], 0))
+ continue
+
+ if no_device_in_range(xbar, dev["name"], result[-1], e):
+ # Determine if size can be power of 2 value
+ smallest_addr_gte = get_next_base_addr(e["base_addr"], xbar,
+ dev["name"])
+
+ # Choose next value
+ if smallest_addr_gte == -1:
+ next_value = 0x100000000
+ else:
+ next_value = int(smallest_addr_gte["base_addr"], 0)
+
+ calc_size = int(e["base_addr"], 0) + int(e["size_byte"], 0) - int(
+ result[-1]["base_addr"], 0)
+
+ # find power of 2 if possible
+ size_byte = find_pow2_size(result[-1], calc_size, next_value)
+
+ result[-1]["size_byte"] = "0x{:x}".format(size_byte)
+ continue
+
+ # If overlapping (Should it be consider? TlGen will catch it)
+
+ # Normal case
+ result.append(e)
+
+ # return result
+ return result
+
+
+def no_device_in_range(xbar, name, f, t):
+ """Check if other devices doesn't overlap with the range 'from <= x < to'
+ """
+ from_addr = int(f["base_addr"], 0) + int(f["size_byte"], 0)
+ to_addr = int(t["base_addr"], 0)
+
+ for node in [
+ x for x in xbar["nodes"]
+ if x["type"] == "device" and not x["name"] == name
+ ]:
+ if not "addr_range" in node:
+ # Xbar?
+ log.info("Xbar type node cannot be compared in this version.",
+ "Please use in caution")
+ continue
+ assert isinstance(node["addr_range"], list)
+
+ for addr in node["addr_range"]:
+ b_addr = int(addr["base_addr"], 0)
+ e_addr = b_addr + int(addr["size_byte"], 0)
+
+ if e_addr <= from_addr or b_addr >= to_addr:
+ # No overlap
+ continue
+ return False
+ return True
+
+
+def get_next_base_addr(addr, xbar, name):
+ """Return the least value of base_addr of the IP greater than addr
+
+ """
+ if isinstance(addr, str):
+ value = int(addr, 0)
+ else:
+ assert isinstance(addr, int)
+ value = addr
+
+ device_list = [
+ x for x in xbar["nodes"]
+ if x["type"] == "device" and not x["name"] == name
+ ]
+
+ try:
+ addrs = [a for r in device_list for a in r["addr_range"]]
+ except KeyError:
+ log.error("Address range is wrong.\n {}".format(
+ [x for x in device_list if not "addr_range" in x]))
+ raise SystemExit()
+
+ sorted_list = sorted(addrs, key=lambda k: int(k["base_addr"], 0))
+
+ gte_list = [x for x in sorted_list if int(x["base_addr"], 0) > value]
+
+ if len(gte_list) == 0:
+ return -1
+
+ return gte_list[0]
+
+
+def find_pow2_size(addr, min_size, next_value):
+ """Find smallest power of 2 value greater than min_size by given addr.
+
+ For instance, {addr:0x4000_0000, min_size:0x21000} and `next_value` as
+ 0x40080000, the result will be 0x4_0000
+
+ But it should return result not exceeding the base_addr's leading one bit
+ position. For instance, if the base_addr is 0x4003_0000, the return value
+ should be less than or equal to 0x1_0000. Cannot be 0x4_0000. So, this
+ case, the function returns the original min_size value 0x21000.
+ """
+ base_addr = int(addr["base_addr"], 0)
+
+ diff = next_value - base_addr
+
+ # Find the least one bit position.
+ # If base_addr is 0, then any value can be used
+ if not base_addr == 0:
+ leading_one = 1
+ while True:
+ if base_addr & leading_one != 0:
+ break
+ leading_one = leading_one << 1
+
+ if leading_one <= diff:
+ diff = leading_one
+
+ i = 1
+ while True:
+ i = i << 1
+ if i >= min_size:
+ break
+
+ # If found pow2 value is greater than diff, it cannot be used. Just use
+ # min_size then the tool will use comparators (>=, <=)
+ if i > diff:
+ i = min_size
+
+ # Shall be greater than 4kB
+ assert i >= 0x1000
+
+ return i
diff --git a/util/tlgen/validate.py b/util/tlgen/validate.py
index c8d9fc2..7f80b92 100644
--- a/util/tlgen/validate.py
+++ b/util/tlgen/validate.py
@@ -10,6 +10,8 @@
from .item import Edge, Node, NodeType
from .xbar import Xbar
+from .lib import simplify_addr
+
# val_types = {
# 'd': ["int", "integer (binary 0b, octal 0o, decimal, hex 0x)"],
# 'x': ["xint", "x for undefined otherwise int"],
@@ -252,6 +254,10 @@
node.xbar = nodeobj["xbar"]
node.addr_range = []
+ # Compact the address ranges
+ if node.xbar:
+ nodeobj["addr_range"] = simplify_addr(nodeobj, obj)
+
for addr in nodeobj["addr_range"]:
address_from = int(addr["base_addr"], 0)
size = int(addr["size_byte"], 0)
diff --git a/util/topgen/merge.py b/util/topgen/merge.py
index b5f0235..41d0932 100644
--- a/util/topgen/merge.py
+++ b/util/topgen/merge.py
@@ -411,187 +411,6 @@
return result
-def simplify_xbar(xbars):
- """ Simplify the address of xbar device port.
-
- It cannot be done without fully expanded addresses.
- So, this should be done after adding all xbar addresses.
- """
-
- for xbar in xbars:
- xdev_list = [
- x for x in xbar["nodes"] if x["type"] == "device" and x["xbar"]
- ]
-
- for e in xdev_list:
- e["addr_range"] = simplify_addr(e, xbar)
-
-
-# TODO: Move to tlgen
-def simplify_addr(dev, xbar):
- """Reduce the number of entries smaller by combining them
-
- If any contiguous regions exist, concatenate them.
- For instance, 0x1000 ~ 0x1FFF , 0x2000~ 0x2FFF ==> 0x1000 ~ 0x2FFF
-
- It also checks if there's no device between the gap, then merge
- the ranges. For instance:
-
- {0x4000_0000, 0x1_0000}, {0x4008_0000, 0x1_0000} then it combines two
- entries into {0x4000_0000, 0x9_0000}
-
- @param addrs List of Dict[Addr] : {'base_addr':,'size_byte':}
- """
-
- addrs = dev["addr_range"]
- # Sort based on the base addr
- newlist = sorted(addrs, key=lambda k: int(k['base_addr'], 0))
- # check if overlap or contiguous
- result = []
- for e in newlist:
- if len(result) == 0:
- result.append(e)
- continue
- # if contiguous
- if int(e["base_addr"], 0) == int(result[-1]["base_addr"], 0) + int(
- result[-1]["size_byte"], 0):
- # update previous entry size
- result[-1]["size_byte"] = "0x{:x}".format(
- int(result[-1]["size_byte"], 0) + int(e["size_byte"], 0))
- continue
-
- if no_device_in_range(xbar, dev["name"], result[-1], e):
- # Determine if size can be power of 2 value
- smallest_addr_gte = get_next_base_addr(e["base_addr"], xbar,
- dev["name"])
-
- # Choose next value
- if smallest_addr_gte == -1:
- next_value = 0x100000000
- else:
- next_value = int(smallest_addr_gte["base_addr"], 0)
-
- calc_size = int(e["base_addr"], 0) + int(e["size_byte"], 0) - int(
- result[-1]["base_addr"], 0)
-
- # find power of 2 if possible
- size_byte = find_pow2_size(result[-1], calc_size, next_value)
-
- result[-1]["size_byte"] = "0x{:x}".format(size_byte)
- continue
-
- # If overlapping (Should it be consider? TlGen will catch it)
-
- # Normal case
- result.append(e)
-
- # return result
- return result
-
-
-def no_device_in_range(xbar, name, f, t):
- """Check if other devices doesn't overlap with the from <= x < to
- """
- from_addr = int(f["base_addr"], 0) + int(f["size_byte"], 0)
- to_addr = int(t["base_addr"], 0)
-
- for node in [
- x for x in xbar["nodes"]
- if x["type"] == "device" and not x["name"] == name
- ]:
- if not "addr_range" in node:
- # Xbar?
- log.info("Xbar type node cannot be compared in this version.",
- "Please use in caution")
- continue
- assert isinstance(node["addr_range"], list)
-
- for addr in node["addr_range"]:
- b_addr = int(addr["base_addr"], 0)
- e_addr = b_addr + int(addr["size_byte"], 0)
-
- if e_addr <= from_addr or b_addr >= to_addr:
- # No overlap
- continue
- return False
- return True
-
-
-def get_next_base_addr(addr, xbar, name):
- """Return the least value of base_addr of the IP greater than addr
-
- """
- if isinstance(addr, str):
- value = int(addr, 0)
- else:
- assert isinstance(addr, int)
- value = addr
-
- device_list = [
- x for x in xbar["nodes"]
- if x["type"] == "device" and not x["name"] == name
- ]
-
- try:
- addrs = [a for r in device_list for a in r["addr_range"]]
- except KeyError:
- log.error("Address range is wrong.\n {}".format(
- [x for x in device_list if not "addr_range" in x]))
- raise SystemExit()
-
- sorted_list = sorted(addrs, key=lambda k: int(k["base_addr"], 0))
-
- gte_list = [x for x in sorted_list if int(x["base_addr"], 0) > value]
-
- if len(gte_list) == 0:
- return -1
-
- return gte_list[0]
-
-
-def find_pow2_size(addr, min_size, next_value):
- """Find smallest power of 2 value greater than min_size by given addr.
-
- For instance, (0x4000_0000, 0x21000) and `next_value` as 0x40080000,
- the result will be 0x4_0000
-
- But it should return result not exceeding the base_addr's leading one bit
- position. For instance, if the base_addr is 0x4003_0000, the return value
- should be less than or equal to 0x1_0000. Cannot be 0x4_0000.
- """
- base_addr = int(addr["base_addr"], 0)
-
- diff = next_value - base_addr
-
- # Find the least one bit position.
- # If base_addr is 0, then any value can be used
- if not base_addr == 0:
- leading_one = 1
- while True:
- if base_addr & leading_one != 0:
- break
- leading_one = leading_one << 1
-
- if leading_one <= diff:
- diff = leading_one
-
- i = 1
- while True:
- i = i << 1
- if i >= min_size:
- break
-
- # If found pow2 value is greater tha diff, it cannot be used. Just use
- # min_size then the tool will use comparators (>=, <=)
- if i > diff:
- i = min_size
-
- # Shall be greater than 4kB
- assert i >= 0x1000
-
- return i
-
-
def amend_interrupt(top):
"""Check interrupt_module if exists, or just use all modules
"""
@@ -780,8 +599,6 @@
for xbar in gencfg["xbar"]:
xbar_cross(xbar, gencfg["xbar"])
- simplify_xbar(gencfg["xbar"])
-
# remove unwanted fields 'debug_mem_base_addr'
gencfg.pop('debug_mem_base_addr', None)