headergen_v2: add floating instances and nochild flag

Floating instances don't have an address and will only generate
child nodes and registers with offsets. The 'nochild' flag will
disable generating the children for a node but will generate the
node's own address, which can be used to generate base addresses.

Change-Id: Ib1014de94531436d5708db46aa684741e7740ace
This commit is contained in:
Aidan MacDonald 2025-01-27 00:58:11 +00:00 committed by Solomon Peachy
parent 0f5c2877fe
commit 387f67cab6
5 changed files with 109 additions and 25 deletions

View file

@ -24,3 +24,4 @@ $(LIB):
clean:
rm -fr $(EXEC)
make -C lib clean

View file

@ -146,7 +146,43 @@ are:
- F[2] at 0x50+(2/2)*0x100+(2%2)*0x10 = 0x50+0x100 = 0x150
- F[3] at 0x50+(3/2)*0x100+(3%2)*0x10 = 0x50+0x100+0x10 = 0x160
1.3) Node description
1.3) Floating and 'nochild' instances
-------------------------------------
Floating instances do not have an fixed address and instead "float" relative
to a base address provided at runtime. Child nodes and registers are generated
as usual, except that their addresses will not be defined, only their offsets
relative to their floating parent node.
<node>
<name>N</name>
<instance>
<name>F</name>
<floating>1</floating>
</instance>
</node>
Instances with the 'nochild' flag will emit their own base address, but won't
generate any child nodes or registers themselves. They can be used alongside
floating nodes to provide the base addresses for SoC peripheral instances:
<node>
<name>N</name>
<instance>
<name>F</name>
<nochild>1</nochild>
<address>0x1234</address>
</instance>
</node>
Nochild instances can use both <address> and <range> for specifying their
address.
For generic code, storing the base address and computing offsets can generate
more efficient code compared to indexed macros that calculate the base address,
especially when the addresses don't follow a simple formula.
1.4) Node description
---------------------
For documentation purposes, node can of course carry some description, as well
@ -177,7 +213,7 @@ The following example illustrates this:
</instance>
</node>
1.4) Register description
1.5) Register description
--------------------------
The goal of the register description is of course to describe registers!
@ -247,7 +283,7 @@ In this example, the 8-bit registers has three fields:
- PRIORITY(2:1): it has no named values
- ARM_MODE(3): it has two named values IRQ(0) and FIQ(1)
1.5) Register inheritance
1.6) Register inheritance
-------------------------
The node hierarchy specifies instances, that is pairs of (path,address),
@ -286,7 +322,7 @@ This example describes one register (let's call it blabla) and 9 instances:
- DMAC.I2C_CHAN.CLR at 0x80000018, register blabla
- DMAC.I2C_CHAN.TOG at 0x8000001C, register blabla
1.6) Soc description
1.7) Soc description
--------------------
The description file must also specify some information about the system-on-chip
@ -352,8 +388,10 @@ It can contain at most one of each of the following tags:
- desc: free form description of the instance
- address: address for a single instance (non-negative number)
- range: address range for multiple instances
Note that address and range are mutually exclusive, and at least one of them
must exists.
- floating: 0 or 1, if 1 then the instance is floating and has no address
- nochild: 0 or 1, if 1 then no child instances/registers are generated
Note that address, range, and floating are mutually exclusive, and at least
one of them must exist. An instance cannot be both floating and nochild.
Element: range
--------------

View file

@ -887,6 +887,8 @@ std::string common_generator::register_address(const pseudo_node_inst_t& reg,
}
else if(inst.type == instance_t::SINGLE)
oss << to_hex(inst.addr);
else if (inst.type == instance_t::FLOATING)
return "";
else
return "#error unknown instance type";
}
@ -911,6 +913,7 @@ bool common_generator::generate_register(std::ostream& os, const pseudo_node_ins
std::string bfm_prefix = macro_basename(reg, MT_FIELD_BFM) + field_prefix();
std::string bfv_prefix = macro_basename(reg, MT_FIELD_BFV) + field_prefix();
std::string bfmv_prefix = macro_basename(reg, MT_FIELD_BFMV) + field_prefix();
bool has_addr = !addr.empty();
register_ref_t regr = reg.inst.node().reg();
/* handle register the same way as variants */
@ -923,7 +926,8 @@ bool common_generator::generate_register(std::ostream& os, const pseudo_node_ins
var_prefix.push_back("");
var_suffix.push_back("");
var_access.push_back(register_access("", regr.get()->access));
var_addr.push_back(addr);
if(has_addr)
var_addr.push_back(addr);
var_offset.push_back(offset);
std::vector< variant_ref_t > variants = regr.variants();
@ -931,9 +935,12 @@ bool common_generator::generate_register(std::ostream& os, const pseudo_node_ins
{
var_prefix.push_back(variant_xfix(variants[i].type(), true));
var_suffix.push_back(variant_xfix(variants[i].type(), false));
var_addr.push_back("(" + type_xfix(MT_REG_ADDR, true) + basename +
type_xfix(MT_REG_ADDR, false) + addr_param_str + " + " +
to_hex(variants[i].offset()) + ")");
if (has_addr)
{
var_addr.push_back("(" + type_xfix(MT_REG_ADDR, true) + basename +
type_xfix(MT_REG_ADDR, false) + addr_param_str + " + " +
to_hex(variants[i].offset()) + ")");
}
var_offset.push_back("(" + type_xfix(MT_REG_OFFSET, true) + basename +
type_xfix(MT_REG_OFFSET, false) + offset_param_str + " + " +
to_hex(variants[i].offset()));
@ -962,16 +969,26 @@ bool common_generator::generate_register(std::ostream& os, const pseudo_node_ins
* if we have support macros then we generate something like HW(basename)
* where HW is some support macros to support complex operations. Otherwise
* we just generate something like (*(volatile unsigned uintN_t)basename_addr) */
if(!has_support_macros())
if (has_addr)
{
std::ostringstream oss;
oss << "(*(volatile uint" << regr.get()->width << "_t *)" << macro_addr + addr_param_str << ")";
ctx.add(macro_var + addr_param_str, oss.str());
if(!has_support_macros())
{
std::ostringstream oss;
oss << "(*(volatile uint" << regr.get()->width << "_t *)" <<
macro_addr + addr_param_str << ")";
ctx.add(macro_var + addr_param_str, oss.str());
}
else
{
ctx.add(macro_var + addr_param_str, macro_name(MN_VARIABLE) +
"(" + var_basename + addr_param_str + ")");
}
/* print ADDR macro */
ctx.add(macro_addr + addr_param_str, var_addr[i]);
}
else
ctx.add(macro_var + addr_param_str, macro_name(MN_VARIABLE) + "(" + var_basename + addr_param_str + ")");
/* print ADDR macro */
ctx.add(macro_addr + addr_param_str, var_addr[i]);
if(has_offsets())
{
@ -1030,7 +1047,6 @@ bool common_generator::generate_register(std::ostream& os, const pseudo_node_ins
bool common_generator::generate_node(std::ostream& os, const pseudo_node_inst_t& node)
{
os << "\n";
define_align_context_t ctx;
std::vector< std::string > params;
std::string addr = register_address(node, params);
@ -1038,8 +1054,13 @@ bool common_generator::generate_node(std::ostream& os, const pseudo_node_inst_t&
std::string param_str = generate_param_str(params);
std::string macro_addr = type_xfix(MT_NODE_ADDR, true) + basename +
type_xfix(MT_NODE_ADDR, false);
bool has_addr = !addr.empty();
ctx.add(macro_addr + param_str, addr);
if (has_addr)
{
os << "\n";
ctx.add(macro_addr + param_str, addr);
}
ctx.print(os);
return true;

View file

@ -230,7 +230,8 @@ struct instance_t
enum type_t
{
SINGLE, /** There is a single instance at a specified address */
RANGE /** There are multiple addresses forming a range */
RANGE, /** There are multiple addresses forming a range */
FLOATING, /** Instance generates child register offsets/fields only */
};
soc_id_t id; /** ID (must be unique among node instances) */
@ -240,9 +241,10 @@ struct instance_t
type_t type; /** Instance type */
soc_word_t addr; /** Address (for SINGLE) */
range_t range; /** Range (for RANGE) */
bool nochild; /** Disable child node generation if set */
/** Default constructor: single instance at 0 */
instance_t():id(DEFAULT_ID), type(SINGLE), addr(0) {}
instance_t():id(DEFAULT_ID), type(SINGLE), addr(0), nochild(false) {}
};
/** Node information */
@ -544,6 +546,8 @@ public:
node_inst_t child(const std::string& name, size_t index) const;
/** Returns a list of all instances of subnodes of this node's instance */
std::vector< node_inst_t > children() const;
/** Check if the instance's nochild flag is set */
bool is_nochild() const;
/** Returns the name of the instance */
std::string name() const;
/** Checks whether this instance is indexed */

View file

@ -458,24 +458,37 @@ bool parse_instance_elem(xmlNode *node, instance_t& inst, error_context_t& ctx)
{
bool ret = true;
bool has_name = false, has_title = false, has_desc = false, has_range = false;
bool has_address = false;
bool has_address = false, has_floating = false, has_nochild = false;
unsigned floating = 0, nochild = 0;
BEGIN_NODE_MATCH(node->children)
MATCH_UNIQUE_TEXT_NODE("name", inst.name, has_name, parse_name_elem, ctx)
MATCH_UNIQUE_TEXT_NODE("title", inst.title, has_title, parse_text_elem, ctx)
MATCH_UNIQUE_TEXT_NODE("desc", inst.desc, has_desc, parse_text_elem, ctx)
MATCH_UNIQUE_TEXT_NODE("nochild", nochild, has_nochild, parse_unsigned_elem, ctx)
MATCH_UNIQUE_TEXT_NODE("floating", floating, has_floating, parse_unsigned_elem, ctx)
MATCH_UNIQUE_TEXT_NODE("address", inst.addr, has_address, parse_unsigned_elem, ctx)
MATCH_UNIQUE_ELEM_NODE("range", inst.range, has_range, parse_range_elem, ctx)
MATCH_UNUSED_NODE(parse_unknown_elem, ctx)
END_NODE_MATCH()
CHECK_HAS(node, "name", has_name, ctx)
if(!has_address && !has_range)
if(!has_address && !has_range && !floating)
ret = ret && parse_missing_error(node, "address> or <range", ctx);
if(has_address && has_range)
ret = ret && parse_conflict_error(node, "address", "range", ctx);
if(has_address)
if(floating && has_address)
ret = ret && parse_conflict_error(node, "floating", "address", ctx);
if(floating && has_range)
ret = ret && parse_conflict_error(node, "floating", "range", ctx);
if(floating && nochild)
ret = ret && parse_conflict_error(node, "floating", "nochild", ctx);
if(floating)
inst.type = instance_t::FLOATING;
else if(has_address)
inst.type = instance_t::SINGLE;
else
inst.type = instance_t::RANGE;
if (nochild)
inst.nochild = true;
return ret;
}
@ -1620,7 +1633,7 @@ node_inst_t node_inst_t::child(const std::string& name) const
node_inst_t node_inst_t::child(const std::string& name, size_t index) const
{
std::vector< node_t > *nodes = get_children(m_node);
if(nodes == 0)
if(nodes == 0 || is_nochild())
return node_inst_t();
node_ref_t child_node = m_node;
for(size_t i = 0; i < nodes->size(); i++)
@ -1648,7 +1661,7 @@ std::vector< node_inst_t > node_inst_t::children() const
std::vector< node_t > *nodes = get_children(m_node);
std::vector< soc_id_t > n_path = m_id_path;
std::vector< size_t > i_path = m_index_path;
if(nodes == 0)
if(nodes == 0 || is_nochild())
return list;
node_ref_t child_node = m_node;
for(size_t i = 0; i < nodes->size(); i++)
@ -1661,6 +1674,7 @@ std::vector< node_inst_t > node_inst_t::children() const
n_path.push_back(inst.id);
switch(inst.type)
{
case instance_t::FLOATING:
case instance_t::SINGLE:
i_path.push_back(INST_NO_INDEX);
list.push_back(node_inst_t(child_node, n_path, i_path));
@ -1684,6 +1698,12 @@ std::vector< node_inst_t > node_inst_t::children() const
return list;
}
bool node_inst_t::is_nochild() const
{
instance_t *inst = get();
return inst == 0 ? false : inst->nochild;
}
std::string node_inst_t::name() const
{
instance_t *inst = get();