csrGen is a perl script to generate verilog code to implement a set of registers for processor read/write access in an ASIC or FPGA design. Automating the code generation from a template file provides these advantages: - less error prone (I've seen code mistakes where bits in a register were swapped between read and write, or missing for either read or write, and there are many other possible mistakes), - template is simpler and faster to prepare and modify - template can also be used to generate documentation and/or tests The script has many features for great flexibility. Any type of register I've ever seen or implemented in a design is possible and probably easier with this script. The template file format specifies global parameters, specific address contents, and verilog code to be incorporated into the block. For an introduction, look at example.csrs. The syntax is line oriented, not fully free. Lines starting with "#" are ignored as comments. Blank lines are ignored. %B specifies the module (block) name in the output verilog, and the file name. The default module name is chip_lp. %C specifies a clock name. Default is "clock". The script currently uses just one clock and reset. The clock is used as a positive edge for flops. %RST specifies a reset name. Default is "initl". Currently all declared flops are asynchronously reset (or set). %AM specifies an address multiple, default is 1. If not 1, then all declared addresses must be multiples of this parameter, and the declared addresses are divided by this parameter for the internal case statement. (This is useful when addresses are documented as byte addresses even though accesses are always as words or long words). %WD and %RD specify names for data busses to be used in verilog case statements for read and write assignments - RD defaults to "up_datao" and is the destination for all read data (the script just builds a single large mux structure at present); WD defaults to "up_dataQ" and is the source of write data. %I declares an input to the module. An optional parameter gives it a width (default being 1 bit wide). %O declares an output from the module. Width is an optional parameter. %W declares a wire, causing a verilog wire declaration. (Width optional). %R causes a verilog reg declaration (width optional). (Not to be confused with the registers we are setting up for the processor.) %F declares a flop. Both "name" and "name_D" become reg declarations, the flop will automatically be part of a "posedge clock" structure as "name <= name_D;", and a default value of "name_D" will be "name". The first parameter is "name", second is the width (default 1), third is a reset value (default 0), and the fourth is a direct value (which would replace the use of "name_D"). %OF declares a flop that is also an output of the module, same syntax as %F. All flops, inputs, and wires become part of a single (large/enormous) sensitivity list for a "always @" block for most all of the combinational logic in the module. %V starts a section of verilog code that will be inserted in the module after all declarations. %E terminates the code. This would be used for instantiations, task or function code, or assign statements. %VCL starts the verilog code to be inserted in the combinational "always @" block, following the long sensitivity list and the default "name_D" values. Within this block of code, %READCASE and %WRITECASE will be expanded with the case statements for processor reads and writes, respectively (but excluding the case() and endcase statements, so the user can directly control the case parameter). %INTRLOGIC will be expanded with fields declared as interrupts and an interrupt mask. %A is the declaration of an address, with fields to be declared on the following lines. The first parameter is the numeric address as a HEX value. An optional second parameter is the name of a verilog task to be called when the address is read - default is "-", meaning no task for this address. The optional third parameter is a task to call when the address is written, default is "-", same meaning. These tasks allow reads and writes to trigger actions beyond basic register accesses. The tasks are called after the basic access, so they can alter the outcome of the basic access. Also allowed declarations for the address lines are: "ro": that all fields at the address are readonly; " "cor": that all fields at the address are sticky and clear on read "w1c": that all fields at the address are sticky and clear when written as 1 and any string enclosed in quotation marks: "xyz blah" will be ignored. (These four items can follow or preceed the read/write task names). The fields within a given address are declared one per line, with the bit subscript within the address specified first, then the name, then additional parameters: - any number is taken as a reset value - "ro" - the field is read-only, so it is an input to the module (default is that a field is read/write, and is an output of the module) - "cor" - register view is a sticky copy on an input to the module, which is cleared when read - "w1c" - register view is a sticky copy on an input to the module, which is cleared when written as 1 - "st" - register view is a sticky copy on an input to the module, clearing the value is either by a direct write, or by user constructed code - "intern" - the field should not be taken as module IO, declaration of the field is left to the user (often a %F would be used). - "intr" - the field is sticky and contributes to an interrupt vector in the bit position that it appears in the register - "intrmask" - the field is a mask for whether interrupt fields propagate to an interrupt. %INTRLOGIC/intr/intrmask are a somewhat focused implementation at present and may not suit everyone's needs. - "sub" and "subm" are used to allow a very wide field to span several register addresses - they are followed by a range (x:y) that represents which subrange of the larger field is represented by the current field. "subm" is used on the most significant part of the field and results in all of the related field and flop declarations. %AREPEAT is a special form of %A that represents a repeating register structure. The first parameter is take as the starting address in hex, the second parameter (also required) is the repeat count in hex, and the optional third parameter is the address increment for each repeat. The repeat index is appended to each field name. "buss", "robuss", "bussintern" and "robussintern" are special cases for 1 bit wide fields where the repeat index becomes a range subscript of a single bus.