1
0
Fork 0
forked from len0rd/rockbox
foxbox/utils/regtools/headergen_v2.cpp
Aidan MacDonald 387f67cab6 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
2025-04-20 15:53:54 -04:00

2305 lines
84 KiB
C++

/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2015 by Amaury Pouly
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "soc_desc.hpp"
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <map>
#include <sstream>
#include <sys/stat.h>
#include <sys/types.h>
#include <getopt.h>
#include <fstream>
#include <set>
using namespace soc_desc;
#define HEADERGEN_VERSION "3.0.0"
/**
* Useful stuff
*/
std::string tolower(const std::string s)
{
std::string res = s;
for(size_t i = 0; i < s.size(); i++)
res[i] = ::tolower(res[i]);
return res;
}
std::string toupper(const std::string& s)
{
std::string res = s;
for(size_t i = 0; i < s.size(); i++)
res[i] = ::toupper(res[i]);
return res;
}
template< typename T >
std::string to_str(const T& v)
{
std::ostringstream oss;
oss << v;
return oss.str();
}
template< typename T >
std::string to_hex(const T& v)
{
std::ostringstream oss;
oss << "0x" << std::hex << v;
return oss.str();
}
std::string strsubst(const std::string& str, const std::string& pattern,
const std::string& subst)
{
std::string res = str;
size_t pos = 0;
while((pos = res.find(pattern, pos)) != std::string::npos)
{
res.replace(pos, pattern.size(), subst);
pos += subst.size();
}
return res;
}
void print_context(const error_context_t& ctx)
{
for(size_t j = 0; j < ctx.count(); j++)
{
err_t e = ctx.get(j);
switch(e.level())
{
case err_t::INFO: printf("[INFO]"); break;
case err_t::WARNING: printf("[WARN]"); break;
case err_t::FATAL: printf("[FATAL]"); break;
default: printf("[UNK]"); break;
}
if(e.location().size() != 0)
printf(" %s:", e.location().c_str());
printf(" %s\n", e.message().c_str());
}
}
void print_inst(node_inst_t inst, bool end = true)
{
if(!inst.is_root())
{
print_inst(inst.parent(), false);
printf(".%s", inst.name().c_str());
if(inst.is_indexed())
printf("[%u]", (unsigned)inst.index());
}
else
{
printf("%s", inst.soc().get()->name.c_str());
}
if(end)
printf("\n");
}
void create_dir(const std::string& path)
{
mkdir(path.c_str(), 0755);
}
void create_alldir(const std::string& prefix, const std::string& path)
{
size_t p = path.find('/');
if(p == std::string::npos)
return;
create_dir(prefix + "/" + path.substr(0, p));
create_alldir(prefix + "/" + path.substr(0, p), path.substr(p + 1));
}
/**
* Printer utils
*/
struct limited_column_context_t
{
limited_column_context_t(size_t nr_col = 80)
:m_nr_col(nr_col), m_prevent_wordcut(true) {}
void set_prefix(const std::string& prefix) { m_prefix = prefix; }
void add(const std::string& text)
{
for(size_t i = 0; i < text.size();)
{
size_t offset = 0;
if(m_cur_line.size() == 0)
m_cur_line = m_prefix;
size_t len = std::min(text.size() - i, m_nr_col - m_cur_line.size());
// prevent word cut
if(m_prevent_wordcut && !isspace(text[i + len - 1]) &&
i + len < text.size() && !isspace(text[i + len]))
{
size_t pos = text.find_last_of(" \t\n\v\r\f", i + len - 1);
if(pos == std::string::npos || pos < i)
len = 0;
else
len = pos - i + 1;
}
size_t pos = text.find('\n', i);
if(pos != std::string::npos && pos <= i + len)
{
offset = 1;
len = pos - i;
}
m_cur_line += text.substr(i, len);
// len == 0 means we need a new line
if(m_cur_line.size() == m_nr_col || len == 0)
{
m_lines.push_back(m_cur_line);
m_cur_line = "";
}
i += len + offset;
}
}
std::ostream& print(std::ostream& oss)
{
for(size_t i = 0; i < m_lines.size(); i++)
oss << m_lines[i] << "\n";
if(m_cur_line.size() != 0)
oss << m_cur_line << "\n";
return oss;
}
std::string str()
{
std::ostringstream oss;
print(oss);
return oss.str();
}
std::vector< std::string > m_lines;
std::string m_cur_line;
std::string m_prefix;
size_t m_nr_col;
bool m_prevent_wordcut;
};
struct define_align_context_t
{
define_align_context_t():m_max_name(0) {}
void add(const std::string& name, const std::string& val)
{
m_lines.push_back(std::make_pair(name, val));
m_max_name = std::max(m_max_name, name.size());
}
void add_raw(const std::string& line)
{
m_lines.push_back(std::make_pair("", line));
}
std::ostream& print(std::ostream& oss)
{
std::string define = "#define ";
size_t align = define.size() + m_max_name + 1;
align = ((align + 3) / 4) * 4;
for(size_t i = 0; i < m_lines.size(); i++)
{
std::string name = m_lines[i].first;
// raw entry ?
if(name.size() != 0)
{
name.insert(name.end(), align - define.size() - name.size(), ' ');
oss << define << name << m_lines[i].second << "\n";
}
else
oss << m_lines[i].second;
}
return oss;
}
size_t m_max_name;
std::vector< std::pair< std::string, std::string > > m_lines;
};
limited_column_context_t print_description(const std::string& desc, const std::string& prefix)
{
limited_column_context_t ctx;
if(desc.size() == 0)
return ctx;
ctx.set_prefix(prefix);
ctx.add(desc);
return ctx;
}
void print_copyright(std::ostream& fout, const std::vector< soc_ref_t >& socs)
{
fout << "\
/***************************************************************************\n\
* __________ __ ___.\n\
* Open \\______ \\ ____ ____ | | _\\_ |__ _______ ___\n\
* Source | _// _ \\_/ ___\\| |/ /| __ \\ / _ \\ \\/ /\n\
* Jukebox | | ( <_> ) \\___| < | \\_\\ ( <_> > < <\n\
* Firmware |____|_ /\\____/ \\___ >__|_ \\|___ /\\____/__/\\_ \\\n\
* \\/ \\/ \\/ \\/ \\/\n\
* This file was automatically generated by headergen, DO NOT EDIT it.\n\
* headergen version: " HEADERGEN_VERSION "\n";
for(size_t i = 0; i < socs.size(); i++)
{
soc_t& s = *socs[i].get();
if(!s.version.empty())
fout << " * " << s.name << " version: " << s.version << "\n";
if(!s.author.empty())
{
fout << " * " << s.name << " authors:";
for(size_t j = 0; j < s.author.size(); j++)
{
if(j != 0)
fout << ",";
fout << " " << s.author[j];
}
fout << "\n";
}
}
fout << "\
*\n\
* Copyright (C) 2015 by the authors\n\
*\n\
* This program is free software; you can redistribute it and/or\n\
* modify it under the terms of the GNU General Public License\n\
* as published by the Free Software Foundation; either version 2\n\
* of the License, or (at your option) any later version.\n\
*\n\
* This software is distributed on an \"AS IS\" basis, WITHOUT WARRANTY OF ANY\n\
* KIND, either express or implied.\n\
*\n\
****************************************************************************/\n";
}
void print_guard(std::ostream& fout, const std::string& guard, bool begin)
{
if(begin)
{
fout << "#ifndef " << guard << "\n";
fout << "#define " << guard << "\n";
}
else
fout << "\n#endif /* " << guard << "*/\n";
}
/**
* Generator interface
*/
class abstract_generator
{
public:
/// set output directory (default is current directory)
void set_output_dir(const std::string& dir);
/// add a SoC to the list
void add_soc(const soc_t& soc);
/// generate headers, returns true on success
virtual bool generate(error_context_t& ctx) = 0;
protected:
std::vector< soc_t > m_soc; /// list of socs
std::string m_outdir; /// output directory path
};
void abstract_generator::set_output_dir(const std::string& dir)
{
m_outdir = dir;
}
void abstract_generator::add_soc(const soc_t& soc)
{
m_soc.push_back(soc);
}
/**
* Common Generator
*
* This generator is an abstract class which can generate register headers while
* giving the user a lot of control on how the macros are named and generated.
*
* The first thing the generator will want to know is whether you want to generate
* selector headers or not. Selector headers are used when generating headers from
* several SoCs: the selector will include the right header based on some user-defined
* logic. For example, imagine you have two socs vsoc1000 and vsoc2000, you could
* generate the following files and directories:
*
* regs/
* regs-vsoc.h [selector]
* vsoc1000/
* regs-vsoc.h [vsoc1000 header]
* vsoc2000/
* regs-vsoc.h [vsoc2000 header]
*
* The generator will call has_selectors() to determine if it should generate
* selector files or not. If it returns true, it will call selector_soc_dir()
* for each of the socs to know in which subdirectory it should include the headers.
* The generator will create one macro per soc, which name is given by
* selector_soc_macro() and it will finally include the select header YOU will
* have to write, which name is given by selector_include_header()
* The selector file will typically look like this:
*
* [regs-vsoc.h]
* #define VSOC1000_INCLUDE "vsoc1000/regs-vsoc.h" // returned by selector_soc_macro(vsoc1000)
* #define VSOC2000_INCLUDE "vsoc2000/regs-vsoc.h" // returned by selector_soc_macro(vsoc2000)
* #include "regs-select.h" // returned by selector_include_header()
*
* NOTE: it may happen that some header only exists in some soc (for example
* in vsoc2000 but not in vsoc1000), in this case the macros will only be
* generated for the socs in which it exists.
* NOTE: and this is *VERY* important: the register selector file MUST NOT
* be protected by include guards since it will included several times, once
* for each register file.
*
* The generator will create one set of files per soc. For each register, it
* will call register_header() to determine in which file you want the register
* to be put. You can put everything in one file, or one register per file,
* that's entirely up to you. Here is an example:
*
* register_header(vsoc1000.cpu.ctrl) -> "regs-cpu.h"
* register_header(vsoc1000.cpu.reset) -> "regs-cpu.h"
* register_header(vsoc1000.cop.ctrl) -> "regs-cop.h"
* register_header(vsoc1000.cop.magic) -> "regs-magic.h"
*
* regs-cpu.h:
* vsoc1000.cpu.ctrl
* vsoc1000.cpu.reset
* regs-cop.h:
* vsoc1000.cop.ctrl
* regs-magic.h
* vsoc1000.cop.magic
*
* Once the list of register files is determine, it will begin generating each
* file. A register header is always protected from double inclusion by an
* include guard. You can tweak the name by redefining header_include_guard()
* or keep the default behaviour which generates something like this:
*
* #ifndef __HEADERGEN_REGS_CPU_H__
* #define __HEADERGEN_REGS_CPU_H__
* ...
* #endif
*
* NOTE: the tool will also generate a copyright header which contains the Rockbox
* logo, a list of soc names and version, and authors in the description file.
* This part cannot be customised at the moment.
*
* In order to list all registers, the generator will recursively enumerate all
* nodes and instances. When a instance is of RANGE type, the generator can generate
* two types of macros:
* - one for each instance
* - one with an index parameter
* The generator will ask you if you want to generate one for each instance by
* calling register_flag(RF_GENERATE_ALL_INST) and if you want to generate a
* parametrized one by calling register_flag(RF_GENERATE_PARAM_INST). You can
* generate either one or both (or none).
*
* register_flag(vsoc.int.enable, RF_GENERATE_ALL_INST) -> true
* => will generate vsoc.int.enable[1], vsoc.int.enable[2], ...
* register_flag(vsoc.int.enable, RF_GENERATE_PARAM_INST) -> true
* => will generate vsoc.int.enable[n]
*
* This process will give a list of pseudo-instances: these are register instances
* where some ranges have a given index and some ranges are parametric. The generator
* will create one set of macro per pseudo-instance. The exact list of macros
* generated is the following:
* - ADDR: this macro will contain the address of the register (possibly parametrized)
* - TYPE: this macro will contain the type of the register (width, access)
* - NAME: this macro expands to the name of the register (useful for fields and other variants)
* - INDEX: this macro expands to the index of the register (or empty is not indexed)
* - VAR: this macros expands to a fake variable that can be read/written
* - for each field F:
* - BP: this macro contains the bit position of F
* - BM: this macro contains the bit mask of F
* - for each field enum value V:
* - BV: this macro contains the value of V (not shifted to the position)
* - BF: this macro generate the mask for a value: (((v) << pos) & mask)
* - BFV: same as BF but the parameter is the name of an enum value
* - BFM: this macro is like BF but ignores value returns mask
* - BFMV: like BFV but returns masks like BFM
* The generator will also create the following macros for each variant:
* - ADDR: same as ADDR but with offset
* - TYPE: same as TYPE but depends on variant
* - NAME: same as NAME (ie same fields)
* - INDEX: same as INDEX
*
* In order to generate the macro name, the generate relies on you providing detailled
* information. Given an pseudo-instance I and a macro type MT, the generator
* will always call type_xfix(MT) to know which prefix/suffix you want for the
* macro and generate names of the form:
* type_xfix(MT, true)basename(I)type_xfix(MT, false)
* The basename() functions will call inst_prefix() for each each instance on the
* pseudo-instance path, and then instance_name() to know the name. You can
* (and must) create a different name for parametrised instances. The basename
* looks like this for pseudo-inst i1.i2.i3....in:
* instance_name(i1)inst_prefix(i1.i2)instance_name(i1.i2)...inst_name(i1.i2...in)
* For field macros, the process is the same with an extra prefix returned by
* field_prefix() and you can select the field name with field_name() to obtain
* for example for field F in I:
* type_xfix(MT,true)basename(I)field_prefix()field_name(I.F)type_xfix(MT,false)
* For field enum macros, there is an extra prefix given by enum_prefix()
* and the enum name is given by enum_name()
* For variants, the basename is surrounded by prefix/suffix given by variant_xfix():
* variant_xfix(var, true)basename(I)variant_xfix(var, false)
*
* Let's illustrate this on example
* type_xfix(MT_REG_ADDR, true) -> "RA_x"
* type_xfix(MT_REG_ADDR, false) -> "x_AR"
* instance_name(vsoc1000.cpu) -> "CPU"
* inst_prefix(vsoc1000.cpu.ctrl) -> "_"
* instance_name(vsoc1000.cpu.ctrl) -> "CTRL"
* => RA_xCPU_CTRLx_AR
* type_xfix(MT_FIELD_BF, true) -> "BF_"
* type_xfix(MT_FIELD_BF, false) -> ""
* field_prefix() -> "__"
* field_name(vsoc1000.cpu.ctrl.speed) -> "SPEED"
* => BF_CPU_CTRL__SPEED
* variant_xfix("set", true) -> "VAR_"
* variant_xfix("set", false) -> "_SET"
* => HW_VAR_CPU_CTRL_SET
*
* The generator will also create a macro header file, it will call macro_header()
* once to know the name of this file.
* The macro header contains useful macro to read, write and manipulate registers
* and fields. You must implement the macro_header_macro() method to let the
* generator know the name of each macro. Note that the macro header macros
* depend on the specific naming of the other macros, which are given by
* type_xfix() and field_prefix() most notably. The exact list of generated macros
* is the following:
* - BF_OR: field ORing (see details below)
* - BF_OM: mask ORing (same as BF_OR but except field value is the mask)
*
* The BF_OR macro is one of the most important and useful macro. It allows for
* compact ORing of register field, both for immediate values or value names.
* For this macro to work properly, there are constraints that the generator
* must satisfy. Notably, type_xfix(MT_FIELD_BF, true) == type_xfix(MT_FIELD_BFV, true)
* and similarly for MT_FIELD_BM
* The general format is as follows:
*
* BF_OR(<reg>, <f1>, <f2>, ...) expands to BF(<reg>,<f1>) | BF(<reg>,<f2>) | ...
* BM_OR(<reg>, <f1>, <f2>, ...) expands to BM(<reg>,<f1>) | BM(<reg>,<f2>) | ...
*
* Typical usages of this macro will be of the form:
*
* BF_OR(<reg>, <f1>(<v1>), <f2>(<v2>), <f3name>(<v3name>), ...)
* BM_OR(<reg>, <f1>, <f2>, <f3>, ...)
*
*
* Let's illustrate this on example.
* type_xfix(MT_FIELD_BF, true) -> "BF_"
* type_xfix(MT_FIELD_BFV, true) -> "BF_"
* type_xfix(MT_FIELD_BF, false) -> "BF_"
* type_xfix(MT_FIELD_BF, false) -> "_V"
* field_prefix() -> "__"
* enum_prefix() -> "___"
* macro_header_macro(MMM_BF_OR) -> "BF_OR"
* => BF_OR(<reg>, <f1>, <f2>, ...) expands to BF_<reg>__<f1> | BF_<reg>__<f2> | ...
* => BF_OR(CPU_CTRL, DIVIDER(1), PRIO_V(HIGH), SPEED(100))
* expands to
* BF_CPU_CTRL__DIVIDER(1) | BF_CPU_CTRL__DIVIDER_V(HIGH) | BF_CPU_CTRL__SPEED(1000)
* expands to
* BF_CPU_CTRL__DIVIDER(1) | BF_CPU_CTRL__DIVIDER(BV_CPU_CTRL__DIVIDER___HIGH) | BF_CPU_CTRL__SPEED(1000)
* and so on...
*
*/
class common_generator : public abstract_generator
{
struct pseudo_node_inst_t
{
node_inst_t inst;
std::vector< bool > parametric;
};
public:
virtual bool generate(error_context_t& ctx);
private:
void gather_files(const pseudo_node_inst_t& inst, const std::string& prefix,
std::map< std::string, std::vector< pseudo_node_inst_t > >& map);
void print_inst(const pseudo_node_inst_t& inst, bool end = true); // debug
std::vector< soc_ref_t > list_socs(const std::vector< pseudo_node_inst_t >& list);
bool generate_register(std::ostream& os, const pseudo_node_inst_t& reg);
bool generate_node(std::ostream& os, const pseudo_node_inst_t& node);
std::string generate_param_str(const std::vector<std::string>& params);
bool generate_macro_header(error_context_t& ectx);
protected:
/// return true to generate support macros
/// if false then all macros are disabled except MT_REG_ADDR, MT_REG_VAR,
/// and MT_FIELD_*. Also the macro header is not created
virtual bool has_support_macros() const = 0;
/// return true to generate selector files
virtual bool has_selectors() const = 0;
/// return true to generate node address and register offset macros
virtual bool has_offsets() const = 0;
/// [selector only] return the directory name for the soc
virtual std::string selector_soc_dir(const soc_ref_t& ref) const = 0;
/// [selector only] return the header to include to select betweens socs
virtual std::string selector_include_header() const = 0;
/// [selector only] return the name of the macro to emit for each soc
virtual std::string selector_soc_macro(const soc_ref_t& ref) const = 0;
/// return the relative filename in which to put the register
virtual std::string register_header(const node_inst_t& inst) const = 0;
/// return the include guard for a file (default does its best)
virtual std::string header_include_guard(const std::string& filename);
/// return the name of the macros header to generate
virtual std::string macro_header() const = 0;
/// macro from macro header
enum macro_name_t
{
MN_REG_READ, /// register read
MN_FIELD_READ, /// register's field read
MN_FIELD_READX, /// register value field read
MN_REG_WRITE, /// register write
MN_REG_SET, /// register set
MN_REG_CLEAR, /// register clear
MN_REG_CLEAR_SET, /// register clear then set
MN_FIELD_WRITE, /// register's field(s) write
MN_FIELD_WRITEX, /// register's field(s) write
MN_FIELD_OVERWRITE, /// register full write
MN_FIELD_SET, /// register's field(s) set
MN_FIELD_CLEAR, /// register's field(s) clear
MN_FIELD_TOG, /// register's field(s) toggle
MN_FIELD_CLEAR_SET, /// register's field(s) clear then set
MN_FIELD_OR, /// field ORing
MN_FIELD_OR_MASK, /// field ORing but in fact mask ORing
MN_MASK_OR, /// mask ORing
MN_GET_VARIANT, /// get register variant
MN_VARIABLE, /// return variable-like for register
};
/// return macro name defined in the macro header
virtual std::string macro_name(macro_name_t macro) const = 0;
/// flag to consider
enum register_flag_t
{
RF_GENERATE_ALL_INST, /// for range instance, generate one macro set per instance ?
RF_GENERATE_PARAM_INST, /// for range instance, generate a parametrised macro set ?
};
/** tweak instance generaton and its children
* NOTE: for range flags, although the instance will be numbered, the index is
* to be ignored */
virtual bool register_flag(const node_inst_t& inst, register_flag_t flag) const = 0;
/// prefix/suffix type
enum macro_type_t
{
MT_NODE_ADDR, /// node address
MT_REG_ADDR, /// register address
MT_REG_OFFSET, /// register address relative to parent node
MT_REG_TYPE, /// register type
MT_REG_NAME, /// register prefix for fields
MT_REG_INDEX, /// register index/indices
MT_REG_VAR, /// variable-like macro
MT_FIELD_BP, /// bit position of a field
MT_FIELD_BM, /// bit mask of a field
MT_FIELD_BV, /// bit value of a field enum
MT_FIELD_BF, /// bit field value: (v << pos) & mask
MT_FIELD_BFM, /// ignore value and return mask
MT_FIELD_BFV, /// bit field enum value: (enum_v << pos) & mask
MT_FIELD_BFMV, /// ignore value and return mask
MT_IO_TYPE, /// register io type
};
/// register access types
enum access_type_t
{
AT_RO, /// read-only: write are disallowed
AT_RW, /// read-write: read/writes are allowed, field write by generated a RMW
AT_WO, /// write-only: read are disallowed, field write will set other fields to 0
};
/// register operation
enum register_op_t
{
RO_VAR, /// variable-like
RO_READ, /// read
RO_WRITE, /// write
RO_RMW, /// read-modify-write
};
/// return type prefix/suffix for register macro
virtual std::string type_xfix(macro_type_t type, bool prefix) const = 0;
/// return variant prefix/suffix
virtual std::string variant_xfix(const std::string& variant, bool prefix) const = 0;
/// return instance prefix in macro name
virtual std::string inst_prefix(const node_inst_t& inst) const = 0;
/// return field prefix in field macro names
virtual std::string field_prefix() const = 0;
/// return field enum prefix in field macro names
virtual std::string enum_prefix() const = 0;
/// return the field enum name in field macro names
virtual std::string enum_name(const enum_ref_t& enum_) const = 0;
/// return instance name in macro, default is instance name then index if any
virtual std::string instance_name(const node_inst_t& inst, bool parametric) const;
/// return field name in macro, default is field name
virtual std::string field_name(const field_ref_t& field) const;
/// return the complete macro name with prefix, default uses all the other functions
virtual std::string macro_basename(const pseudo_node_inst_t& inst) const;
/// return the complete macro name with prefix, default uses macro_basename()
virtual std::string macro_basename(const pseudo_node_inst_t& inst, macro_type_t type) const;
/// generate address string for a register instance, and fill the parametric
/// argument list, default does it the obvious way and parameters are _n1, _n2, ...
virtual std::string register_address(const pseudo_node_inst_t& reg,
std::vector< std::string >& params, bool offset_only = false) const;
/// return access type for a variant and a given register access
/// NOTE variant with the unspecified access type will be promoted to register access
virtual access_type_t register_access(const std::string& variant, access_t access) const = 0;
/// return register type name
virtual std::string register_type_name(access_type_t access, int width) const;
/// get register operation name
virtual std::string register_op_name(register_op_t op) const;
/// register operation prefix
virtual std::string register_op_prefix() const;
/// generate a macro pasting that is safe even when macro is empty
/// ie: safe_macro_paste(false, "A") -> ##A
/// ie: safe_macro_paste(false, "A") ->
std::string safe_macro_paste(bool left, const std::string& macro) const;
/// generate SCT macros using register variant ?
virtual bool has_sct() const = 0;
/// return the associated SCT variant (only if RF_GENERATE_SCT)
/// empty means don't generate
virtual std::string sct_variant(macro_name_t name) const = 0;
};
std::string common_generator::instance_name(const node_inst_t& inst, bool parametric) const
{
std::ostringstream oss;
oss << inst.name();
if(inst.is_indexed() && !parametric)
oss << inst.index();
return oss.str();
}
std::string common_generator::field_name(const field_ref_t& field) const
{
return field.get()->name;
}
std::string common_generator::macro_basename(const pseudo_node_inst_t& inst_) const
{
pseudo_node_inst_t inst = inst_;
std::string str;
while(!inst.inst.is_root())
{
str = instance_name(inst.inst, inst.parametric.back()) + str;
inst.inst = inst.inst.parent();
inst.parametric.pop_back();
if(!inst.inst.is_root())
str = inst_prefix(inst.inst) + str;
}
return str;
}
std::string common_generator::macro_basename(const pseudo_node_inst_t& inst, macro_type_t type) const
{
return type_xfix(type, true) + macro_basename(inst);
}
void common_generator::print_inst(const pseudo_node_inst_t& inst, bool end)
{
if(!inst.inst.is_root())
{
pseudo_node_inst_t p = inst;
p.inst = p.inst.parent();
p.parametric.pop_back();
print_inst(p, false);
printf(".%s", inst.inst.name().c_str());
if(inst.inst.is_indexed())
{
if(inst.parametric.back())
printf("[]");
else
printf("[%u]", (unsigned)inst.inst.index());
}
}
else
{
printf("%s", inst.inst.soc().get()->name.c_str());
}
if(end)
printf("\n");
}
std::string common_generator::register_type_name(access_type_t access, int width) const
{
std::ostringstream oss;
oss << width << "_";
switch(access)
{
case AT_RO: oss << "RO"; break;
case AT_RW: oss << "RW"; break;
case AT_WO: oss << "WO"; break;
default: oss << "error"; break;
}
return type_xfix(MT_IO_TYPE, true) + oss.str() + type_xfix(MT_IO_TYPE, false);
}
std::string common_generator::register_op_name(register_op_t op) const
{
switch(op)
{
case RO_VAR: return "VAR";
case RO_READ: return "RD";
case RO_WRITE: return "WR";
case RO_RMW: return "RMW";
default: return "<op>";
}
}
std::string common_generator::register_op_prefix() const
{
return "_";
}
std::string common_generator::safe_macro_paste(bool left, const std::string& macro) const
{
if(macro.size() == 0)
return "";
return left ? "##" + macro : macro + "##";
}
void common_generator::gather_files(const pseudo_node_inst_t& inst, const std::string& prefix,
std::map< std::string, std::vector< pseudo_node_inst_t > >& map)
{
if(!inst.inst.is_root())
map[prefix + register_header(inst.inst)].push_back(inst);
// if asked, generate one for each instance
std::vector< node_inst_t > list = inst.inst.children();
for(size_t i = 0; i < list.size(); i++)
{
pseudo_node_inst_t c = inst;
c.inst = list[i];
if(!list[i].is_indexed() || register_flag(inst.inst, RF_GENERATE_ALL_INST))
{
c.parametric.push_back(false);
gather_files(c, prefix, map);
c.parametric.pop_back();
}
if(list[i].is_indexed() && register_flag(list[i], RF_GENERATE_PARAM_INST))
{
bool first = list[i].index() == list[i].get()->range.first;
if(first)
{
c.parametric.push_back(true);
gather_files(c, prefix, map);
}
}
}
}
std::vector< soc_ref_t > common_generator::list_socs(const std::vector< pseudo_node_inst_t >& list)
{
std::set< soc_ref_t > socs;
std::vector< pseudo_node_inst_t >::const_iterator it = list.begin();
for(; it != list.end(); ++it)
socs.insert(it->inst.soc());
std::vector< soc_ref_t > soc_list;
for(std::set< soc_ref_t >::iterator jt = socs.begin(); jt != socs.end(); ++jt)
soc_list.push_back(*jt);
return soc_list;
}
std::string common_generator::header_include_guard(const std::string& filename)
{
std::string guard = "__HEADERGEN_" + toupper(filename) + "__";
for(size_t i = 0; i < guard.size(); i++)
if(!isalnum(guard[i]))
guard[i] = '_';
return guard;
}
std::string common_generator::register_address(const pseudo_node_inst_t& reg,
std::vector< std::string >& params, bool offset_only) const
{
std::ostringstream oss;
unsigned counter = 1;
size_t start_index = offset_only ? reg.parametric.size() - 1 : 0;
oss << "(";
for(size_t i = start_index; i < reg.parametric.size(); i++)
{
if(i != start_index)
oss << " + ";
node_inst_t ninst = reg.inst.parent(reg.parametric.size() - i - 1);
instance_t& inst = *ninst.get();
if(reg.parametric[i])
params.push_back("_n" + to_str(counter++));
if(inst.type == instance_t::RANGE)
{
if(inst.range.type == range_t::STRIDE)
{
if(reg.parametric[i])
{
oss << to_hex(inst.range.base) << " + ";
oss << "(" << params.back() << ") * " << to_hex(inst.range.stride);
}
else
oss << to_hex(inst.range.base + ninst.index() * inst.range.stride);
}
else if(inst.range.type == range_t::FORMULA)
{
if(!reg.parametric[i])
{
soc_word_t res;
std::map< std::string, soc_word_t > vars;
vars[inst.range.variable] = ninst.index();
error_context_t ctx;
if(!evaluate_formula(inst.range.formula, vars, res, "", ctx))
oss << "#error cannot evaluate formula";
else
oss << to_hex(res);
}
else
oss << strsubst(inst.range.formula, inst.range.variable,
"(" + params.back() + ")");
}
else if(inst.range.type == range_t::LIST)
{
if(reg.parametric[i])
{
oss << "(";
for(size_t j = 0; j + 1 < inst.range.list.size(); j++)
{
oss << "(" << params.back() << ") == " << (inst.range.first + j)
<< " ? " << to_hex(inst.range.list[j]) << " : ";
}
oss << to_hex(inst.range.list.back()) << ")";
}
else
oss << to_hex(inst.range.list[ninst.index() - inst.range.first]);
}
else
{
return "#error unknown range type";
}
}
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";
}
oss << ")";
return oss.str();
}
bool common_generator::generate_register(std::ostream& os, const pseudo_node_inst_t& reg)
{
os << "\n";
define_align_context_t ctx;
std::vector< std::string > addr_params;
std::vector< std::string > offset_params;
std::string addr = register_address(reg, addr_params);
std::string offset = register_address(reg, offset_params, true);
std::string basename = macro_basename(reg);
std::string addr_param_str = generate_param_str(addr_params);
std::string offset_param_str = generate_param_str(offset_params);
std::string bp_prefix = macro_basename(reg, MT_FIELD_BP) + field_prefix();
std::string bm_prefix = macro_basename(reg, MT_FIELD_BM) + field_prefix();
std::string bf_prefix = macro_basename(reg, MT_FIELD_BF) + field_prefix();
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 */
std::vector< std::string > var_prefix;
std::vector< std::string > var_suffix;
std::vector< std::string > var_addr;
std::vector< std::string > var_offset;
std::vector< access_type_t > var_access;
var_prefix.push_back("");
var_suffix.push_back("");
var_access.push_back(register_access("", regr.get()->access));
if(has_addr)
var_addr.push_back(addr);
var_offset.push_back(offset);
std::vector< variant_ref_t > variants = regr.variants();
for(size_t i = 0; i < variants.size(); i++)
{
var_prefix.push_back(variant_xfix(variants[i].type(), true));
var_suffix.push_back(variant_xfix(variants[i].type(), false));
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()));
access_t acc = variants[i].get()->access;
if(acc == UNSPECIFIED)
acc = regr.get()->access; // fallback to register access
var_access.push_back(register_access(variants[i].type(), acc));
}
for(size_t i = 0; i < var_prefix.size(); i++)
{
std::string var_basename = var_prefix[i] + basename + var_suffix[i];
std::string macro_var = type_xfix(MT_REG_VAR, true) + var_basename +
type_xfix(MT_REG_VAR, false);
std::string macro_addr = type_xfix(MT_REG_ADDR, true) + var_basename +
type_xfix(MT_REG_ADDR, false);
std::string macro_offset = type_xfix(MT_REG_OFFSET, true) + var_basename +
type_xfix(MT_REG_OFFSET, false);
std::string macro_type = type_xfix(MT_REG_TYPE, true) + var_basename +
type_xfix(MT_REG_TYPE, false);
std::string macro_prefix = type_xfix(MT_REG_NAME, true) + var_basename +
type_xfix(MT_REG_NAME, false);
std::string macro_index = type_xfix(MT_REG_INDEX, true) + var_basename +
type_xfix(MT_REG_INDEX, false);
/* print VAR macro:
* 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_addr)
{
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 + ")");
if(has_offsets())
{
/* print OFFSET macro */
ctx.add(macro_offset + offset_param_str, var_offset[i]);
}
/* disable macros if needed */
if(has_support_macros())
{
/* print TYPE macro */
ctx.add(macro_type + addr_param_str, register_type_name(var_access[i], regr.get()->width));
/* print PREFIX macro */
ctx.add(macro_prefix + addr_param_str, basename);
/* print INDEX macro */
ctx.add(macro_index + addr_param_str, addr_param_str);
}
}
/* print fields */
std::vector< field_ref_t > fields = regr.fields();
for(size_t i = 0; i < fields.size(); i++)
{
/* print BP macro: pos */
std::string fname = field_name(fields[i]);
ctx.add(bp_prefix + fname + type_xfix(MT_FIELD_BP, false),
to_str(fields[i].get()->pos));
/* print BM macro: mask */
ctx.add(bm_prefix + fname + type_xfix(MT_FIELD_BM, false),
to_hex(fields[i].get()->bitmask()));
std::vector< enum_ref_t > enums = fields[i].enums();
std::string bv_prefix = macro_basename(reg, MT_FIELD_BV) + field_prefix()
+ fname + enum_prefix();
/* print BV macros: enum_v */
for(size_t j = 0; j < enums.size(); j++)
ctx.add(bv_prefix + enum_name(enums[j]) + type_xfix(MT_FIELD_BV, false),
to_hex(enums[j].get()->value));
/* print BF macro: (((v) & mask) << pos) */
ctx.add(bf_prefix + fname + type_xfix(MT_FIELD_BF, false) + "(v)",
"(((v) & " + to_hex(fields[i].get()->unshifted_bitmask()) + ") << " +
to_str(fields[i].get()->pos) + ")");
/* print BFM macro: masl */
ctx.add(bfm_prefix + fname + type_xfix(MT_FIELD_BFM, false) + "(v)",
bm_prefix + fname + type_xfix(MT_FIELD_BM, false));
/* print BFV macro: ((enum_v) << pos) & mask) */
ctx.add(bfv_prefix + fname + type_xfix(MT_FIELD_BFV, false) + "(e)",
bf_prefix + fname + type_xfix(MT_FIELD_BF, false) + "(" +
bv_prefix + "##e)");
/* print BFMV macro: masl */
ctx.add(bfmv_prefix + fname + type_xfix(MT_FIELD_BFMV, false) + "(v)",
bm_prefix + fname + type_xfix(MT_FIELD_BM, false));
}
ctx.print(os);
return true;
}
bool common_generator::generate_node(std::ostream& os, const pseudo_node_inst_t& node)
{
define_align_context_t ctx;
std::vector< std::string > params;
std::string addr = register_address(node, params);
std::string basename = macro_basename(node);
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();
if (has_addr)
{
os << "\n";
ctx.add(macro_addr + param_str, addr);
}
ctx.print(os);
return true;
}
std::string common_generator::generate_param_str(const std::vector<std::string>& params)
{
std::string param_str;
if(params.size() > 0)
{
param_str = "(";
for(size_t i = 0; i < params.size(); i++)
param_str += (i == 0 ? "" : ",") + params[i];
param_str += ")";
}
return param_str;
}
bool common_generator::generate_macro_header(error_context_t& ectx)
{
/* only generate if we need support macros */
if(!has_support_macros())
return true;
std::ofstream fout((m_outdir + "/" + macro_header()).c_str());
if(!fout)
{
printf("Cannot create '%s'\n", (m_outdir + "/" + macro_header()).c_str());
return false;
}
print_copyright(fout, std::vector< soc_ref_t >());
std::string guard = header_include_guard(macro_header());
print_guard(fout, guard, true);
fout << "\n";
/* ensure that types uintXX_t are defined */
fout << "#include <stdint.h>\n\n";
/* print variadic OR macros:
* __VAR_OR1(prefix, suffix) expands to prefix##suffix
* and more n>=2, using multiple layers of macros:
* __VAR_ORn(pre, s01, s02, ..., sn) expands to pre##s01 | .. | pre##sn */
std::string var_or = "__VAR_OR";
const int MAX_N = 13;
fout << "#define " << var_or << "1(prefix, suffix) \\\n";
fout << " (prefix##suffix)\n";
for(int n = 2; n <= MAX_N; n++)
{
fout << "#define " << var_or << n << "(pre";
for(int j = 1; j <= n; j++)
fout << ", s" << j;
fout << ") \\\n";
/* dichotmoty: expands ORn using ORj and ORk where j=n/2 and k=n-j */
int half = n / 2;
fout << " (" << var_or << half << "(pre";
for(int j = 1; j <= half; j++)
fout << ", s" << j;
fout << ") | " << var_or << (n - half) << "(pre";
for(int j = half + 1; j <= n; j++)
fout << ", s" << j;
fout << "))\n";
}
fout << "\n";
/* print macro to compute number of arguments */
std::string var_nargs = "__VAR_NARGS";
fout << "#define " << var_nargs << "(...) " << var_nargs << "_(__VA_ARGS__";
for(int i = MAX_N; i >= 1; i--)
fout << ", " << i;
fout << ")\n";
fout << "#define " << var_nargs << "_(";
for(int i = 1; i <= MAX_N; i++)
fout << "_" << i << ", ";
fout << "N, ...) N\n\n";
/* print macro for variadic register macros */
std::string var_expand = "__VAR_EXPAND";
fout << "#define " << var_expand << "(macro, prefix, ...) "
<< var_expand << "_(macro, " << var_nargs << "(__VA_ARGS__), prefix, __VA_ARGS__)\n";
fout << "#define " << var_expand << "_(macro, cnt, prefix, ...) "
<< var_expand << "__(macro, cnt, prefix, __VA_ARGS__)\n";
fout << "#define " << var_expand << "__(macro, cnt, prefix, ...) "
<< var_expand << "___(macro##cnt, prefix, __VA_ARGS__)\n";
fout << "#define " << var_expand << "___(macro, prefix, ...) "
<< "macro(prefix, __VA_ARGS__)\n\n";
/* print type macros */
define_align_context_t ctx;
access_type_t at[3] = { AT_RO, AT_RW, AT_WO };
int width[3] = { 8, 16, 32 };
for(int i = 0; i < 3; i++)
{
for(int j = 0; j < 3; j++)
{
std::string io_type = register_type_name(at[i], width[j]);
ctx.add(io_type + "(op, name, ...)", io_type + register_op_prefix() + "##op(name, __VA_ARGS__)");
// read
std::ostringstream oss;
std::string reg_addr = safe_macro_paste(false, type_xfix(MT_REG_ADDR, true))
+ "name" + safe_macro_paste(true, type_xfix(MT_REG_ADDR, false));
if(at[i] == AT_RO)
oss << "(*(const volatile uint" << width[j] << "_t *)(" << reg_addr << "))";
else if(at[i] == AT_RW)
oss << "(*(volatile uint" << width[j] << "_t *)(" << reg_addr << "))";
else
oss << "({_Static_assert(0, #name \" is write-only\"); 0;})";
ctx.add(io_type + register_op_prefix() + register_op_name(RO_READ) + "(name, ...)",
oss.str());
// write
oss.str("");
if(at[i] != AT_RO)
oss << "(*(volatile uint" << width[j] << "_t *)(" << reg_addr << ")) = (val)";
else
oss << "_Static_assert(0, #name \" is read-only\")";
ctx.add(io_type + register_op_prefix() + register_op_name(RO_WRITE) + "(name, val)",
oss.str());
// read-modify-write
oss.str("");
if(at[i] == AT_RW)
{
oss << io_type << register_op_prefix() << register_op_name(RO_WRITE) + "(name, ";
oss << "(" << io_type << register_op_prefix() << register_op_name(RO_READ) + "(name) & (vand))";
oss << " | (vor))";
}
else if(at[i] == AT_WO)
oss << io_type << register_op_prefix() << register_op_name(RO_WRITE) + "(name, vor)";
else
oss << "_Static_assert(0, #name \" is read-only\")";
ctx.add(io_type + register_op_prefix() + register_op_name(RO_RMW) + "(name, vand, vor)",
oss.str());
// variable
oss.str("");
if(at[i] == AT_RO)
oss << "(*(const volatile uint" << width[j] << "_t *)(" << reg_addr << "))";
else
oss << "(*(volatile uint" << width[j] << "_t *)(" << reg_addr << "))";
ctx.add(io_type + register_op_prefix() + register_op_name(RO_VAR) + "(name, ...)",
oss.str());
ctx.add_raw("\n");
}
}
ctx.print(fout);
fout << "\n";
/* print GET_VARIANT macro */
std::string get_var = macro_name(MN_GET_VARIANT);
fout << "/** " << get_var << "\n";
fout << " *\n";
fout << " * usage: " << get_var << "(register, variant_prefix, variant_postfix)\n";
fout << " *\n";
fout << " * effect: expands to register variant given as argument\n";
fout << " * note: internal usage\n";
fout << " * note: register must be fully qualified if indexed\n";
fout << " *\n";
fout << " * example: " << get_var << "(ICOLL_CTRL, , _SET)\n";
fout << " * example: " << get_var << "(ICOLL_ENABLE(3), , _CLR)\n";
fout << " */\n";
fout << "#define " << get_var << "(name, varp, vars) "
<< get_var << "_(" << safe_macro_paste(false, type_xfix(MT_REG_NAME, true))
<< "name" << safe_macro_paste(true, type_xfix(MT_REG_NAME, false)) << ", "
<< safe_macro_paste(false, type_xfix(MT_REG_INDEX, true))
<< "name" << safe_macro_paste(true, type_xfix(MT_REG_INDEX, false)) << ", varp, vars)\n";
fout << "#define " << get_var << "_(...) " << get_var << "__(__VA_ARGS__)\n";
fout << "#define " << get_var << "__(name, index, varp, vars) "
<< "varp##name##vars index\n";
fout << "\n";
/* print BF_OR macro */
std::string bf_or = macro_name(MN_FIELD_OR);
fout << "/** " << bf_or << "\n";
fout << " *\n";
fout << " * usage: " << bf_or << "(register, f1(v1), f2(v2), ...)\n";
fout << " *\n";
fout << " * effect: expands to the register value where each field fi has value vi.\n";
fout << " * Informally: reg_f1(v1) | reg_f2(v2) | ...\n";
fout << " * note: enumerated values for fields can be obtained by using the syntax:\n";
fout << " * f1" << type_xfix(MT_FIELD_BFV, false) << "(name)\n";
fout << " *\n";
fout << " * example: " << bf_or << "(ICOLL_CTRL, SFTRST(1), CLKGATE(0), TZ_LOCK_V(UNLOCKED))\n";
fout << " */\n";
fout << "#define " << bf_or << "(reg, ...) "
<< var_expand << "(" << var_or << ", " << type_xfix(MT_FIELD_BF, true)
<< "##reg##" << field_prefix() << ", __VA_ARGS__)\n";
fout << "\n";
/* print BF_OR macro */
std::string bfm_or = macro_name(MN_FIELD_OR_MASK);
fout << "/** " << bfm_or << "\n";
fout << " *\n";
fout << " * usage: " << bfm_or << "(register, f1(v1), f2(v2), ...)\n";
fout << " *\n";
fout << " * effect: expands to the register value where each field fi has maximum value (vi is ignored).\n";
fout << " * note: internal usage\n";
fout << " *\n";
fout << " * example: " << bfm_or << "(ICOLL_CTRL, SFTRST(1), CLKGATE(0), TZ_LOCK_V(UNLOCKED))\n";
fout << " */\n";
fout << "#define " << bfm_or << "(reg, ...) "
<< var_expand << "(" << var_or << ", " << type_xfix(MT_FIELD_BFM, true)
<< "##reg##" << field_prefix() << ", __VA_ARGS__)\n";
fout << "\n";
/* print BM_OR macro */
std::string bm_or = macro_name(MN_MASK_OR);
fout << "/** " << bm_or << "\n";
fout << " *\n";
fout << " * usage: " << bm_or << "(register, f1, f2, ...)\n";
fout << " *\n";
fout << " * effect: expands to the register value where each field fi is set to its maximum value.\n";
fout << " * Informally: reg_f1_mask | reg_f2_mask | ...\n";
fout << " *\n";
fout << " * example: " << bm_or << "(ICOLL_CTRL, SFTRST, CLKGATE)\n";
fout << " */\n";
fout << "#define " << bm_or << "(reg, ...) "
<< var_expand << "(" << var_or << ", " << type_xfix(MT_FIELD_BM, true)
<< "##reg##" << field_prefix() << ", __VA_ARGS__)\n\n";
fout << "\n";
/* print REG_READ macro */
std::string reg_read = macro_name(MN_REG_READ);
fout << "/** " << reg_read << "\n";
fout << " *\n";
fout << " * usage: " << reg_read << "(register)\n";
fout << " *\n";
fout << " * effect: read a register and return its value\n";
fout << " * note: register must be fully qualified if indexed\n";
fout << " *\n";
fout << " * example: " << reg_read << "(ICOLL_STATUS)\n";
fout << " * " << reg_read << "(ICOLL_ENABLE(42))\n";
fout << " */\n";
fout << "#define " << reg_read << "(name) "
<< safe_macro_paste(false, type_xfix(MT_REG_TYPE, true)) << "name"
<< safe_macro_paste(true, type_xfix(MT_REG_TYPE, false))
<< "(" << register_op_name(RO_READ) << ", name)\n";
fout << "\n";
/* print FIELD_READX macro */
std::string bf_readx = macro_name(MN_FIELD_READX);
fout << "/** " << bf_readx << "\n";
fout << " *\n";
fout << " * usage: " << bf_readx << "(value, register, field)\n";
fout << " *\n";
fout << " * effect: given a register value, return the value of a particular field\n";
fout << " * note: this macro does NOT read any register\n";
fout << " *\n";
fout << " * example: " << bf_readx << "(0xc0000000, ICOLL_CTRL, SFTRST)\n";
fout << " * " << bf_readx << "(0x46ff, ICOLL_ENABLE, CPU0_PRIO)\n";
fout << " */\n";
fout << "#define " << bf_readx << "(val, name, field) "
<< "(((val) & " << safe_macro_paste(false, type_xfix(MT_FIELD_BM, true))
<< "name" << safe_macro_paste(true, type_xfix(MT_FIELD_BM, false))
<< safe_macro_paste(true, field_prefix()) << "##field"
<< ") >> " << safe_macro_paste(false, type_xfix(MT_FIELD_BP, true))
<< "name" << safe_macro_paste(true, type_xfix(MT_FIELD_BP, false))
<< safe_macro_paste(true, field_prefix()) << "##field"
<< ")\n";
fout << "\n";
/* print FIELD_READ macro */
std::string bf_read = macro_name(MN_FIELD_READ);
fout << "/** " << bf_read << "\n";
fout << " *\n";
fout << " * usage: " << bf_read << "(register, field)\n";
fout << " *\n";
fout << " * effect: read a register and return the value of a particular field\n";
fout << " * note: register must be fully qualified if indexed\n";
fout << " *\n";
fout << " * example: " << bf_read << "(ICOLL_CTRL, SFTRST)\n";
fout << " * " << bf_read << "(ICOLL_ENABLE(3), CPU0_PRIO)\n";
fout << " */\n";
fout << "#define " << bf_read << "(name, field) " << bf_read << "_("
<< reg_read << "(name), " << safe_macro_paste(false, type_xfix(MT_REG_NAME, true))
<< "name" << safe_macro_paste(true, type_xfix(MT_REG_NAME, false)) << ", field)\n";
fout << "#define " << bf_read << "_(...) " << bf_readx << "(__VA_ARGS__)\n";
fout << "\n";
/* print REG_WRITE macro */
std::string reg_write = macro_name(MN_REG_WRITE);
fout << "/** " << reg_write << "\n";
fout << " *\n";
fout << " * usage: " << reg_write << "(register, value)\n";
fout << " *\n";
fout << " * effect: write a register\n";
fout << " * note: register must be fully qualified if indexed\n";
fout << " *\n";
fout << " * example: " << reg_write << "(ICOLL_CTRL, 0x42)\n";
fout << " * " << reg_write << "(ICOLL_ENABLE_SET(3), 0x37)\n";
fout << " */\n";
fout << "#define " << reg_write << "(name, val) "
<< safe_macro_paste(false, type_xfix(MT_REG_TYPE, true)) << "name"
<< safe_macro_paste(true, type_xfix(MT_REG_TYPE, false))
<< "(" << register_op_name(RO_WRITE) << ", name, val)\n";
fout << "\n";
/* print FIELD_WRITE macro */
std::string bf_write = macro_name(MN_FIELD_WRITE);
fout << "/** " << bf_write << "\n";
fout << " *\n";
fout << " * usage: " << bf_write << "(register, f1(v1), f2(v2), ...)\n";
fout << " *\n";
fout << " * effect: change the register value so that field fi has value vi\n";
fout << " * note: register must be fully qualified if indexed\n";
fout << " * note: this macro may perform a read-modify-write\n";
fout << " *\n";
fout << " * example: " << bf_write << "(ICOLL_CTRL, SFTRST(1), CLKGATE(0), TZ_LOCK_V(UNLOCKED))\n";
fout << " * " << bf_write << "(ICOLL_ENABLE(3), CPU0_PRIO(1), CPU0_TYPE_V(FIQ))\n";
fout << " */\n";
fout << "#define " << bf_write << "(name, ...) "
<< bf_write << "_(name, " << safe_macro_paste(false, type_xfix(MT_REG_NAME, true))
<< "name" << safe_macro_paste(true, type_xfix(MT_REG_NAME, false)) << ", __VA_ARGS__)\n";
fout << "#define " << bf_write << "_(name, name2, ...) "
<< safe_macro_paste(false, type_xfix(MT_REG_TYPE, true)) << "name"
<< safe_macro_paste(true, type_xfix(MT_REG_TYPE, false))
<< "(" << register_op_name(RO_RMW) << ", name, "
<< "~" << macro_name(MN_FIELD_OR_MASK) << "(name2, __VA_ARGS__), "
<< macro_name(MN_FIELD_OR) << "(name2, __VA_ARGS__))\n";
fout << "\n";
/* print FIELD_OVERWRITE macro */
std::string bf_overwrite = macro_name(MN_FIELD_OVERWRITE);
fout << "/** " << bf_overwrite << "\n";
fout << " *\n";
fout << " * usage: " << bf_overwrite << "(register, f1(v1), f2(v2), ...)\n";
fout << " *\n";
fout << " * effect: change the register value so that field fi has value vi and other fields have value zero\n";
fout << " * thus this macro is equivalent to:\n";
fout << " * " << reg_write << "(register, " << bf_or << "(register, f1(v1), ...))\n";
fout << " * note: register must be fully qualified if indexed\n";
fout << " * note: this macro will overwrite the register (it is NOT a read-modify-write)\n";
fout << " *\n";
fout << " * example: " << bf_overwrite << "(ICOLL_CTRL, SFTRST(1), CLKGATE(0), TZ_LOCK_V(UNLOCKED))\n";
fout << " * " << bf_overwrite << "(ICOLL_ENABLE(3), CPU0_PRIO(1), CPU0_TYPE_V(FIQ))\n";
fout << " */\n";
fout << "#define " << bf_overwrite << "(name, ...) "
<< bf_overwrite << "_(name, " << safe_macro_paste(false, type_xfix(MT_REG_NAME, true))
<< "name" << safe_macro_paste(true, type_xfix(MT_REG_NAME, false)) << ", __VA_ARGS__)\n";
fout << "#define " << bf_overwrite << "_(name, name2, ...) "
<< safe_macro_paste(false, type_xfix(MT_REG_TYPE, true)) << "name"
<< safe_macro_paste(true, type_xfix(MT_REG_TYPE, false))
<< "(" << register_op_name(RO_WRITE) << ", name, "
<< macro_name(MN_FIELD_OR) << "(name2, __VA_ARGS__))\n";
fout << "\n";
/* print FIELD_WRITEX macro */
std::string bf_writex = macro_name(MN_FIELD_WRITEX);
fout << "/** " << bf_writex << "\n";
fout << " *\n";
fout << " * usage: " << bf_writex << "(var, register, f1(v1), f2(v2), ...)\n";
fout << " *\n";
fout << " * effect: change the variable value so that field fi has value vi\n";
fout << " * note: this macro will perform a read-modify-write\n";
fout << " *\n";
fout << " * example: " << bf_writex << "(var, ICOLL_CTRL, SFTRST(1), CLKGATE(0), TZ_LOCK_V(UNLOCKED))\n";
fout << " * " << bf_writex << "(var, ICOLL_ENABLE, CPU0_PRIO(1), CPU0_TYPE_V(FIQ))\n";
fout << " */\n";
fout << "#define " << bf_writex << "(var, name, ...) "
<< "(var) = " << macro_name(MN_FIELD_OR) << "(name, __VA_ARGS__) | (~"
<< macro_name(MN_FIELD_OR_MASK) << "(name, __VA_ARGS__) & (var))\n";
fout << "\n";
/* print FIELD_SET/FIELD_CLEAR macro */
for(int i = 0; i < 2; i++)
{
macro_name_t n = (i == 0) ? MN_FIELD_SET : MN_FIELD_CLEAR;
std::string bf_set = macro_name(n);
std::string set_var = sct_variant(n);
fout << "/** " << bf_set << "\n";
fout << " *\n";
fout << " * usage: " << bf_set << "(register, f1, f2, ...)\n";
fout << " *\n";
fout << " * effect: change the register value so that field fi has ";
if(i == 0)
fout << "maximum value\n";
else
fout << "value zero\n";
if(has_sct())
fout << " * IMPORTANT: this macro performs a write to the " << set_var << " variant of the register\n";
else
fout << " * note: this macro will perform a read-modify-write\n";
fout << " * note: register must be fully qualified if indexed\n";
fout << " *\n";
fout << " * example: " << bf_set << "(ICOLL_CTRL, SFTRST, CLKGATE)\n";
fout << " * " << bf_set << "(ICOLL_ENABLE(3), CPU0_PRIO, CPU0_TYPE)\n";
fout << " */\n";
if(has_sct())
{
fout << "#define " << bf_set << "(name, ...) " << bf_set << "_("
<< macro_name(MN_GET_VARIANT) << "(name, " << variant_xfix(set_var, true)
<< ", " << variant_xfix(set_var, false) << "), "
<< safe_macro_paste(false, type_xfix(MT_REG_NAME, true))
<< "name" << safe_macro_paste(true, type_xfix(MT_REG_NAME, false))
<< ", __VA_ARGS__)\n";
fout << "#define " << bf_set << "_(name, name2, ...) " << macro_name(MN_REG_WRITE)
<< "(name, " << macro_name(MN_MASK_OR) << "(name2, __VA_ARGS__))\n";
}
else
{
fout << "#define " << bf_set << "(name, ...) "
<< bf_set << "_(name, " << safe_macro_paste(false, type_xfix(MT_REG_NAME, true))
<< "name" << safe_macro_paste(true, type_xfix(MT_REG_NAME, false)) << ", __VA_ARGS__)\n";
fout << "#define " << bf_set << "_(name, name2, ...) "
<< safe_macro_paste(false, type_xfix(MT_REG_TYPE, true)) << "name"
<< safe_macro_paste(true, type_xfix(MT_REG_TYPE, false))
<< "(" << register_op_name(RO_RMW) << ", name, ~";
if(i == 0)
fout << "0," << macro_name(MN_MASK_OR) << "(name2, __VA_ARGS__))\n";
else
fout << macro_name(MN_MASK_OR) << "(name2, __VA_ARGS__), 0)\n";
}
fout << "\n";
}
if(has_sct())
{
std::string set_var = sct_variant(MN_FIELD_SET);
std::string clr_var = sct_variant(MN_FIELD_CLEAR);
/* print REG_SET */
std::string reg_set = macro_name(MN_REG_SET);
fout << "/** " << reg_set << "\n";
fout << " *\n";
fout << " * usage: " << reg_set << "(register, set_value)\n";
fout << " *\n";
fout << " * effect: set some bits using " << set_var << " variant\n";
fout << " * note: register must be fully qualified if indexed\n";
fout << " *\n";
fout << " * example: " << reg_set << "(ICOLL_CTRL, 0x42)\n";
fout << " * " << reg_set << "(ICOLL_ENABLE(3), 0x37)\n";
fout << " */\n";
fout << "#define " << reg_set << "(name, sval) "
<< reg_set << "_(" << macro_name(MN_GET_VARIANT) << "(name, "
<< variant_xfix(set_var, true) << ", " << variant_xfix(set_var, false)
<< "), sval)\n";
fout << "#define " << reg_set << "_(sname, sval) "
<< macro_name(MN_REG_WRITE) << "(sname, sval)\n";
fout << "\n";
/* print REG_CLR */
std::string reg_clr = macro_name(MN_REG_CLEAR);
fout << "/** " << reg_clr << "\n";
fout << " *\n";
fout << " * usage: " << reg_clr << "(register, clr_value)\n";
fout << " *\n";
fout << " * effect: clear some bits using " << clr_var << " variant\n";
fout << " * note: register must be fully qualified if indexed\n";
fout << " *\n";
fout << " * example: " << reg_clr << "(ICOLL_CTRL, 0x42)\n";
fout << " * " << reg_clr << "(ICOLL_ENABLE(3), 0x37)\n";
fout << " */\n";
fout << "#define " << reg_clr << "(name, cval) "
<< reg_clr << "_(" << macro_name(MN_GET_VARIANT) << "(name, "
<< variant_xfix(clr_var, true) << ", " << variant_xfix(clr_var, false)
<< "), cval)\n";
fout << "#define " << reg_clr << "_(cname, cval) "
<< macro_name(MN_REG_WRITE) << "(cname, cval)\n";
fout << "\n";
/* print REG_CS */
std::string reg_cs = macro_name(MN_REG_CLEAR_SET);
fout << "/** " << reg_cs << "\n";
fout << " *\n";
fout << " * usage: " << reg_cs << "(register, clear_value, set_value)\n";
fout << " *\n";
fout << " * effect: clear some bits using " << clr_var << " variant and then set some using " << set_var << " variant\n";
fout << " * note: register must be fully qualified if indexed\n";
fout << " *\n";
fout << " * example: " << reg_cs << "(ICOLL_CTRL, 0xff, 0x42)\n";
fout << " * " << reg_cs << "(ICOLL_ENABLE(3), 0xff, 0x37)\n";
fout << " */\n";
fout << "#define " << reg_cs << "(name, cval, sval) "
<< reg_cs << "_(" << macro_name(MN_GET_VARIANT) << "(name, "
<< variant_xfix(clr_var, true) << ", " << variant_xfix(clr_var, false) << "), "
<< macro_name(MN_GET_VARIANT) << "(name, " << variant_xfix(set_var, true)
<< ", " << variant_xfix(set_var, false) << "), cval, sval)\n";
fout << "#define " << reg_cs << "_(cname, sname, cval, sval) "
<< "do { " << macro_name(MN_REG_WRITE) << "(cname, cval); "
<< macro_name(MN_REG_WRITE) << "(sname, sval); } while(0)\n";
fout << "\n";
/* print BF_CS */
std::string bf_cs = macro_name(MN_FIELD_CLEAR_SET);
fout << "/** " << bf_cs << "\n";
fout << " *\n";
fout << " * usage: " << bf_cs << "(register, f1(v1), f2(v2), ...)\n";
fout << " *\n";
fout << " * effect: change the register value so that field fi has value vi using " << clr_var << " and " << set_var << " variants\n";
fout << " * note: register must be fully qualified if indexed\n";
fout << " * note: this macro will NOT perform a read-modify-write and is thus safer\n";
fout << " * IMPORTANT: this macro will set some fields to 0 temporarily, make sure this is acceptable\n";
fout << " *\n";
fout << " * example: " << bf_cs << "(ICOLL_CTRL, SFTRST(1), CLKGATE(0), TZ_LOCK_V(UNLOCKED))\n";
fout << " * " << bf_cs << "(ICOLL_ENABLE(3), CPU0_PRIO(1), CPU0_TYPE_V(FIQ))\n";
fout << " */\n";
fout << "#define " << bf_cs << "(name, ...) "
<< bf_cs << "_(name, " << safe_macro_paste(false, type_xfix(MT_REG_NAME, true))
<< "name" << safe_macro_paste(true, type_xfix(MT_REG_NAME, false)) << ", __VA_ARGS__)\n";
fout << "#define " << bf_cs << "_(name, name2, ...) "
<< macro_name(MN_REG_CLEAR_SET) << "(name, " << macro_name(MN_FIELD_OR_MASK)
<< "(name2, __VA_ARGS__), " << macro_name(MN_FIELD_OR) << "(name2, __VA_ARGS__))\n";
fout << "\n";
}
/* print REG_VAR macro */
std::string reg_var = macro_name(MN_VARIABLE);
fout << "/** " << reg_var << "\n";
fout << " *\n";
fout << " * usage: " << reg_var << "(register)\n";
fout << " *\n";
fout << " * effect: return a variable-like expression that can be read/written\n";
fout << " * note: register must be fully qualified if indexed\n";
fout << " * note: read-only registers will yield a constant expression\n";
fout << " *\n";
fout << " * example: unsigned x = " << reg_var << "(ICOLL_STATUS)\n";
fout << " * unsigned x = " << reg_var << "(ICOLL_ENABLE(42))\n";
fout << " * " << reg_var << "(ICOLL_ENABLE(42)) = 64\n";
fout << " */\n";
fout << "#define " << reg_var << "(name) "
<< safe_macro_paste(false, type_xfix(MT_REG_TYPE, true)) << "name"
<< safe_macro_paste(true, type_xfix(MT_REG_TYPE, false))
<< "(" << register_op_name(RO_VAR) << ", name)\n";
fout << "\n";
print_guard(fout, guard, false);
fout.close();
return true;
}
bool common_generator::generate(error_context_t& ectx)
{
/* first find which inst goes to which file */
std::map< std::string, std::vector< pseudo_node_inst_t > > regmap;
for(size_t i = 0; i < m_soc.size(); i++)
{
soc_ref_t soc(&m_soc[i]);
pseudo_node_inst_t inst;
inst.inst = soc.root_inst();
gather_files(inst, has_selectors() ? selector_soc_dir(soc) + "/" :
"", regmap);
}
/* create output directory */
create_dir(m_outdir);
/* print files */
std::map< std::string, std::vector< pseudo_node_inst_t > >::iterator it = regmap.begin();
for(; it != regmap.end(); ++it)
{
create_alldir(m_outdir, it->first.c_str());
std::ofstream fout((m_outdir + "/" + it->first).c_str());
if(!fout)
{
printf("Cannot create '%s'\n", (m_outdir + "/" + it->first).c_str());
return false;
}
std::vector< soc_ref_t > soc_list = list_socs(it->second);
print_copyright(fout, soc_list);
std::string guard = header_include_guard(it->first);
print_guard(fout, guard, true);
/* if we generate selectors, we include the macro header in them, otherwise
* we include the macro header right here. If we don't need support macros,
* we also don't include it. */
if(!has_selectors() && has_support_macros())
{
fout << "\n";
fout << "#include \"" << macro_header() << "\"\n";
}
for(size_t i = 0; i < it->second.size(); i++)
{
if(it->second[i].inst.node().reg().valid())
{
if(!generate_register(fout, it->second[i]))
{
printf("Cannot generate register ");
print_inst(it->second[i]);
return false;
}
}
else if(has_offsets() && !it->second[i].inst.node().is_root())
{
if(!generate_node(fout, it->second[i]))
{
printf("Cannot generate node ");
print_inst(it->second[i]);
return false;
}
}
}
print_guard(fout, guard, false);
fout.close();
}
/* for selectors only */
if(has_selectors())
{
/* list all possible headers and per soc headers */
std::map< std::string, std::set< soc_ref_t > > headers;
for(it = regmap.begin(); it != regmap.end(); ++it)
{
/* pick the first instance in the file to extract the file name */
node_inst_t inst = it->second[0].inst;
headers[register_header(inst)].insert(inst.node().soc());
}
/* create selector headers */
std::map< std::string, std::set< soc_ref_t > >::iterator jt;
for(jt = headers.begin(); jt != headers.end(); ++jt)
{
std::ofstream fout((m_outdir + "/" + jt->first).c_str());
if(!fout)
{
printf("Cannot create selector '%s'\n", (m_outdir + "/" + jt->first).c_str());
return false;
}
print_copyright(fout, std::vector< soc_ref_t >());
std::string guard = header_include_guard(jt->first);
print_guard(fout, guard, true);
/* if we generate selectors, we include the macro header in them */
fout << "\n";
fout << "#include \"" << macro_header() << "\"\n";
std::set< soc_ref_t >::iterator kt;
define_align_context_t ctx;
for(kt = jt->second.begin(); kt != jt->second.end(); ++kt)
{
std::ostringstream oss;
oss << "\"" << selector_soc_dir(*kt) << "/" << jt->first << "\"";
ctx.add(selector_soc_macro(*kt), oss.str());
}
fout << "\n";
ctx.print(fout);
fout << "\n";
fout << "#include \"" << selector_include_header() << "\"\n";
fout << "\n";
for(kt = jt->second.begin(); kt != jt->second.end(); ++kt)
fout << "#undef " << selector_soc_macro(*kt) << "\n";
print_guard(fout, guard, false);
fout.close();
}
}
/* generate macro header */
if(!generate_macro_header(ectx))
return false;
return true;
}
/**
* Generator: jz
*/
class jz_generator : public common_generator
{
bool has_support_macros() const
{
return true;
}
bool has_selectors() const
{
return m_soc.size() >= 2;
}
bool has_offsets() const
{
return false;
}
std::string selector_soc_dir(const soc_ref_t& ref) const
{
return ref.get()->name;
}
std::string selector_include_header() const
{
return "select.h";
}
std::string selector_soc_macro(const soc_ref_t& ref) const
{
return toupper(ref.get()->name) + "_INCLUDE";
}
std::string register_header(const node_inst_t& inst) const
{
/* one register header per top-level block */
if(inst.is_root())
return "<error>";
if(inst.parent().is_root())
return tolower(inst.node().name()) + ".h";
else
return register_header(inst.parent());
}
std::string macro_name(macro_name_t macro) const
{
switch(macro)
{
case MN_REG_READ: return "jz_read";
case MN_FIELD_READ: return "jz_readf";
case MN_FIELD_READX: return "jz_vreadf";
case MN_REG_WRITE: return "jz_write";
case MN_REG_SET: return "jz_set";
case MN_REG_CLEAR: return "jz_clr";
case MN_FIELD_WRITE: return "jz_writef";
case MN_FIELD_OVERWRITE: return "jz_overwritef";
case MN_FIELD_WRITEX: return "jz_vwritef";
case MN_FIELD_SET: return "jz_setf";
case MN_FIELD_CLEAR: return "jz_clrf";
case MN_FIELD_TOG: return "jz_togf";
case MN_FIELD_CLEAR_SET: return "jz_csf";
case MN_FIELD_OR: return "jz_orf";
case MN_FIELD_OR_MASK: return "__jz_orfm"; // internal macro
case MN_MASK_OR: return "jz_orm";
case MN_REG_CLEAR_SET: return "jz_cs";
case MN_GET_VARIANT: return "__jz_variant"; // internal macro
case MN_VARIABLE: return "jz_reg";
default: return "<macro_name>";
}
}
std::string macro_header() const
{
return "macro.h";
}
bool register_flag(const node_inst_t& inst, register_flag_t flag) const
{
/* make everything parametrized */
switch(flag)
{
case RF_GENERATE_ALL_INST: return false;
case RF_GENERATE_PARAM_INST: return true;
default: return false;
}
}
std::string type_xfix(macro_type_t type, bool prefix) const
{
switch(type)
{
case MT_REG_ADDR: return prefix ? "JA_" : "";
case MT_REG_TYPE: return prefix ? "JT_" : "";
case MT_REG_NAME: return prefix ? "JN_" : "";
case MT_REG_INDEX: return prefix ? "JI_" : "";
case MT_REG_VAR: return prefix ? "REG_" : "";
case MT_FIELD_BP: return prefix ? "BP_" : "";
case MT_FIELD_BM: return prefix ? "BM_" : "";
case MT_FIELD_BV: return prefix ? "BV_" : "";
case MT_FIELD_BF: return prefix ? "BF_" : "";
case MT_FIELD_BFM: return prefix ? "BFM_" : "";
case MT_FIELD_BFV: return prefix ? "BF_" : "_V";
case MT_FIELD_BFMV: return prefix ? "BFM_" : "_V";
case MT_IO_TYPE: return prefix ? "JIO_" : "";
default: return "<xfix>";
}
}
std::string variant_xfix(const std::string& variant, bool prefix) const
{
/* variant X -> reg_X */
if(prefix)
return "";
else
return "_" + toupper(variant);
}
std::string inst_prefix(const node_inst_t& inst) const
{
/* separate blocks with _: block_reg */
return "_";
}
std::string field_prefix() const
{
/* separate fields with _: block_reg_field */
return "_";
}
std::string enum_prefix() const
{
/* separate enums with __: block_reg_field__enum */
return "__";
}
std::string enum_name(const enum_ref_t& enum_) const
{
return enum_.get()->name;
}
access_type_t register_access(const std::string& variant, access_t access) const
{
/* SET and CLR are special and always promoted to WO */
if(variant == "set" || variant == "clr" || access == WRITE_ONLY)
return AT_WO;
else if(access == READ_ONLY)
return AT_RO;
else
return AT_RW;
}
bool has_sct() const
{
return true;
}
std::string sct_variant(macro_name_t name) const
{
switch(name)
{
case MN_FIELD_SET: return "set"; // always use set variant
case MN_FIELD_CLEAR: return "clr"; // always use clr variant
default: return "";
}
}
};
/**
* Generator: imx
*/
class imx_generator : public common_generator
{
bool has_support_macros() const
{
return true;
}
bool has_selectors() const
{
return m_soc.size() >= 2;
}
bool has_offsets() const
{
return false;
}
std::string selector_soc_dir(const soc_ref_t& ref) const
{
return ref.get()->name;
}
std::string selector_include_header() const
{
return "select.h";
}
std::string selector_soc_macro(const soc_ref_t& ref) const
{
return toupper(ref.get()->name) + "_INCLUDE";
}
std::string register_header(const node_inst_t& inst) const
{
/* one register header per top-level block */
if(inst.is_root())
return "<error>";
if(inst.parent().is_root())
return tolower(inst.node().name()) + ".h";
else
return register_header(inst.parent());
}
std::string macro_name(macro_name_t macro) const
{
switch(macro)
{
case MN_REG_READ: return "REG_RD";
case MN_FIELD_READ: return "BF_RD";
case MN_FIELD_READX: return "BF_RDX";
case MN_REG_WRITE: return "REG_WR";
case MN_REG_SET: return ""; // no macro for this
case MN_REG_CLEAR: return ""; // no macro for this
case MN_FIELD_WRITE: return "BF_WR";
case MN_FIELD_OVERWRITE: return "BF_WR_ALL";
case MN_FIELD_WRITEX: return "BF_WRX";
case MN_FIELD_SET: return "BF_SET";
case MN_FIELD_CLEAR: return "BF_CLR";
case MN_FIELD_TOG: return "BF_TOG";
case MN_FIELD_CLEAR_SET: return "BF_CS";
case MN_FIELD_OR: return "BF_OR";
case MN_FIELD_OR_MASK: return "__BFM_OR";
case MN_MASK_OR: return "BM_OR";
case MN_REG_CLEAR_SET: return "REG_CS";
case MN_GET_VARIANT: return "__REG_VARIANT";
case MN_VARIABLE: return "HW";
default: return "<macro_name>";
}
}
std::string macro_header() const
{
return "macro.h";
}
bool register_flag(const node_inst_t& inst, register_flag_t flag) const
{
/* make everything parametrized */
switch(flag)
{
case RF_GENERATE_ALL_INST: return false;
case RF_GENERATE_PARAM_INST: return true;
default: return false;
}
}
std::string type_xfix(macro_type_t type, bool prefix) const
{
switch(type)
{
case MT_REG_ADDR: return prefix ? "HWA_" : "";
case MT_REG_TYPE: return prefix ? "HWT_" : "";
case MT_REG_NAME: return prefix ? "HWN_" : "";
case MT_REG_INDEX: return prefix ? "HWI_" : "";
case MT_REG_VAR: return prefix ? "HW_" : "";
case MT_FIELD_BP: return prefix ? "BP_" : "";
case MT_FIELD_BM: return prefix ? "BM_" : "";
case MT_FIELD_BV: return prefix ? "BV_" : "";
case MT_FIELD_BF: return prefix ? "BF_" : "";
case MT_FIELD_BFM: return prefix ? "BFM_" : "";
case MT_FIELD_BFV: return prefix ? "BF_" : "_V";
case MT_FIELD_BFMV: return prefix ? "BFM_" : "_V";
case MT_IO_TYPE: return prefix ? "HWIO_" : "";
default: return "<xfix>";
}
}
std::string variant_xfix(const std::string& variant, bool prefix) const
{
/* variant X -> reg_X */
if(prefix)
return "";
else
return "_" + toupper(variant);
}
std::string inst_prefix(const node_inst_t& inst) const
{
/* separate blocks with _: block_reg */
return "_";
}
std::string field_prefix() const
{
/* separate fields with _: block_reg_field */
return "_";
}
std::string enum_prefix() const
{
/* separate enums with __: block_reg_field__enum */
return "__";
}
std::string enum_name(const enum_ref_t& enum_) const
{
return enum_.get()->name;
}
access_type_t register_access(const std::string& variant, access_t access) const
{
/* SET, CLR and TOG are special and always promoted to WO */
if(variant == "set" || variant == "clr" || variant == "tog" || access == WRITE_ONLY)
return AT_WO;
else if(access == READ_ONLY)
return AT_RO;
else
return AT_RW;
}
bool has_sct() const
{
return true;
}
std::string sct_variant(macro_name_t name) const
{
switch(name)
{
case MN_FIELD_SET: return "set"; // always use set variant
case MN_FIELD_CLEAR: return "clr"; // always use clr variant
case MN_FIELD_TOG: return "tog"; // always use tog variant
default: return "";
}
}
};
/**
* Generator: atj
*/
class atj_generator : public common_generator
{
bool has_support_macros() const
{
// no support macros
return false;
}
bool has_selectors() const
{
return false;
}
bool has_offsets() const
{
return false;
}
std::string selector_soc_dir(const soc_ref_t& ref) const
{
return ref.get()->name;
}
std::string selector_include_header() const
{
// unused
return "<error>";
}
std::string selector_soc_macro(const soc_ref_t& ref) const
{
// unused
return "<error>";
}
std::string register_header(const node_inst_t& inst) const
{
/* one register header per top-level block */
if(inst.is_root())
return "<error>";
if(inst.parent().is_root())
return tolower(inst.node().name()) + ".h";
else
return register_header(inst.parent());
}
std::string macro_name(macro_name_t macro) const
{
// no macros are generated
return "<macro_name>";
}
std::string macro_header() const
{
// unused
return "<error>";
}
bool register_flag(const node_inst_t& inst, register_flag_t flag) const
{
/* make everything parametrized */
switch(flag)
{
case RF_GENERATE_ALL_INST: return false;
case RF_GENERATE_PARAM_INST: return true;
default: return false;
}
}
std::string type_xfix(macro_type_t type, bool prefix) const
{
switch(type)
{
case MT_REG_ADDR: return prefix ? "" : "_ADDR";
case MT_REG_VAR: return prefix ? "" : "";
case MT_FIELD_BP: return prefix ? "BP_" : "";
case MT_FIELD_BM: return prefix ? "BM_" : "";
case MT_FIELD_BV: return prefix ? "BV_" : "";
case MT_FIELD_BF: return prefix ? "BF_" : "";
case MT_FIELD_BFM: return prefix ? "BFM_" : "";
case MT_FIELD_BFV: return prefix ? "BF_" : "_V";
case MT_FIELD_BFMV: return prefix ? "BFM_" : "_V";
default: return "<xfix>";
}
}
std::string variant_xfix(const std::string& variant, bool prefix) const
{
return "<variant>";
}
std::string inst_prefix(const node_inst_t& inst) const
{
/* separate blocks with _: block_reg */
return "_";
}
std::string field_prefix() const
{
/* separate fields with _: block_reg_field */
return "_";
}
std::string enum_prefix() const
{
/* separate enums with __: block_reg_field__enum */
return "__";
}
std::string enum_name(const enum_ref_t& enum_) const
{
return enum_.get()->name;
}
access_type_t register_access(const std::string& variant, access_t access) const
{
return AT_RW;
}
bool has_sct() const
{
return false;
}
std::string sct_variant(macro_name_t name) const
{
return "<variant>";
}
};
/**
* Driver
*/
abstract_generator *get_generator(const std::string& name)
{
if(name == "jz")
return new jz_generator();
else if(name == "imx")
return new imx_generator();
else if(name == "atj")
return new atj_generator();
else
return 0;
}
void usage()
{
printf("usage: headergen [options] <desc files...>\n");
printf("options:\n");
printf(" -?/--help Dispaly this help\n");
printf(" -g/--generator <gen> Select generator (jz, imx)\n");
printf(" -o/--outdir <dir> Output directory\n");
exit(1);
}
int main(int argc, char **argv)
{
char *generator_name = NULL;
char *outdir = NULL;
if(argc <= 1)
usage();
while(1)
{
static struct option long_options[] =
{
{"help", no_argument, 0, '?'},
{"generator", required_argument, 0, 'g'},
{"outdir", required_argument, 0, 'o'},
{0, 0, 0, 0}
};
int c = getopt_long(argc, argv, "?g:o:", long_options, NULL);
if(c == -1)
break;
switch(c)
{
case -1:
break;
case '?':
usage();
break;
case 'g':
generator_name = optarg;
break;
case 'o':
outdir = optarg;
break;
default:
abort();
}
}
if(argc == optind)
{
printf("You need at least one description file\n");
return 3;
}
if(outdir == 0)
{
printf("You need to select an output directory\n");
return 4;
}
if(generator_name == 0)
{
printf("You need to select a generator\n");
return 1;
}
abstract_generator *gen = get_generator(generator_name);
if(gen == 0)
{
printf("Unknown generator name '%s'\n", generator_name);
return 2;
}
gen->set_output_dir(outdir);
for(int i = optind; i < argc; i++)
{
error_context_t ctx;
soc_t s;
bool ret = parse_xml(argv[i], s, ctx);
if(ctx.count() != 0)
printf("In file %s:\n", argv[i]);
print_context(ctx);
if(!ret)
{
printf("Cannot parse file '%s'\n", argv[i]);
return 1;
}
gen->add_soc(s);
}
error_context_t ctx;
bool ret = gen->generate(ctx);
print_context(ctx);
if(!ret)
{
printf("Cannot generate headers\n");
return 5;
}
return 0;
}