Commit a7aad2bb authored by Andrey Filippov's avatar Andrey Filippov

Generating lowlevel.c for u-boot

parent b833a922
#!/usr/bin/env python
# Copyright (C) 2013, Elphel.inc.
# Generation of the C source file for u-boot lowlevel init
# 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 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
__author__ = "Andrey Filippov"
__copyright__ = "Copyright 2013, Elphel, Inc."
__license__ = "GPL"
__version__ = "3.0+"
__maintainer__ = "Andrey Filippov"
__email__ = "andrey@elphel.com"
__status__ = "Development"
#import ezynq_clk
class EzynqUBoot:
license="""/*
* This file is automatically generated by the Free Software program using open information from the
* components datasheets, reference manuals and user data.
* No license is required to distribute this file. User may select his/her own license to replace
* this header text.
*/
"""
# remove unneeded
include_section="""
#include <common.h>
#include <asm/io.h>
#include <asm/arch/sys_proto.h>
#include <asm/arch/hardware.h>
"""
def __init__(self, verbosity):
self.cfile=self.license + self.include_section
self.verbosity=verbosity
self.sections=['license','include']
def get_c_file(self):
return self.cfile
def _add_reg_writes(self,reg_sets):
for addr, data, _, module_name, register_name, r_def in reg_sets:
try:
comments=r_def['COMMENTS']
except:
comments=''
self.cfile+='\twritel(0x%08x, 0x%08x); /* %s.%s %s */\n'%(data,addr,module_name,register_name,comments)
def registers_setup (self, reg_sets,clk,num_rbl_regs): #clk is an instance of ezynq_clk.EzynqClk
self.sections.append('registers_setup')
self.cfile+="""
/*
Setup registers after control is passed from the ROM boot loader to the user code.
%i registers are already set up using RBL register initialization feature
*/
inline void register_setup(void)
{
"""%num_rbl_regs
self._add_reg_writes(reg_sets)
self.cfile+='}\n\n'
def pll_setup (self, reg_sets,clk): #clk is an instance of ezynq_clk.EzynqClk
pll_status_comment=clk.clk_register_set.get_register_comments('pll_status')
pll_arm=clk.clk_register_set.get_bitfield_address_mask_comments('pll_status','arm_pll_lock')
pll_ddr=clk.clk_register_set.get_bitfield_address_mask_comments('pll_status','ddr_pll_lock')
pll_io= clk.clk_register_set.get_bitfield_address_mask_comments('pll_status', 'io_pll_lock')
address=pll_arm[0]
mask=0
pll_used=clk.get_plls_used()
for pll, pll_tuple in zip(['ARM','DDR','IO'],[pll_arm,pll_ddr,pll_io]):
if pll in pll_used:
mask |= pll_tuple[1]
if mask==0 :
print 'No PLLs are used, skipping generating pll_setup()'
return
self.sections.append('pll_setup')
self.cfile+='''/* Wait for PLLs locked: %s */
inline void pll_setup(void)
{
\t/* Wait for all PLLs locked */
\twhile ((readl (0x%x) & 0x%x) != 0x%x); /* slcr.pll_status %s */
\t/* release PLL bypass on each PLL */
'''%(str(pll_used),address,mask,mask,pll_status_comment)
self._add_reg_writes(reg_sets)
self.cfile+='}\n\n'
def dci_calibration (self, reg_sets,ddr): #ddr is an instance of ezynq_ddr.EzynqDDR
if len(reg_sets)==0:
print 'No DCI calibration register data is provided, skipping generating dci_calibration()'
return
dci_status_comment=ddr.ddriob_register_set.get_register_comments('ddriob_dci_status')
address,mask,_=ddr.ddriob_register_set.get_bitfield_address_mask_comments('ddriob_dci_status','done')
self.cfile+='''/* Calibrate DDR DCI, wait for completion */
inline void dci_calibration(void)
{
\t/* Toggle active-low DCI reset, initialize DCI calibration */
'''
self._add_reg_writes(reg_sets)
self.cfile+='''\t/* Wait DCI calibration is DONE */
\twhile ((readl (0x%x) & 0x%x) != 0x%x); /* slcr.ddriob_dci_status %s */
'''%(address,mask,mask,dci_status_comment)
self.cfile+='}\n\n'
self.sections.append('dci_calibration')
def ddr_start (self, reg_sets,ddr): #ddr is an instance of ezynq_ddr.EzynqDDR
if len(reg_sets)==0:
print 'No DDR start data is provided, skipping generating ddr_start()'
return
ddrc_status_comment=ddr.ddrc_register_set.get_register_comments('mode_sts_reg')
address,mask,_=ddr.ddrc_register_set.get_bitfield_address_mask_comments('mode_sts_reg','ddrc_reg_operating_mode')
self.cfile+='''/* Start DDRC, wait for initialization complete */
inline void ddr_start(void)
{
\t/* Release DDRC active-low reset */
'''
self._add_reg_writes(reg_sets)
self.cfile+='''\t/* DDRC operation mode is INITIALIZED */
\twhile ((readl (0x%x) & 0x%x) == 0); /* ddrc.mode_sts_reg %s */
'''%(address,mask,ddrc_status_comment)
self.cfile+='}\n\n'
self.sections.append('ddr_start')
def make_lowlevel_init (self):
self.cfile+='''/* Initialize clocks, DDR memory, copy OCM to DDR */
void lowlevel_init(void)
{
/*
Unlock SLCR and write PLL and clocks registers as the code is now running in the OCM and no
peripherals are needed
*/
\tregister_setup();
/*
Wait PLLs locked and turn off bypass - all clocks should have specified values now
*/
\tpll_setup();
/*
Calibrate DDR DCI impedance and wait for completion
*/
\tdci_calibration();
/*
Remove soft reset from DDR controller - that starts initialization. Wait for completion
*/
\tddr_start();
/*
Copy 3 pages of OCM from 0x00000.0x2ffff to DDR 0x4000000.0x402ffff
*/
\tint * s= (int *) 0;
\tint * d= (int *) 0x4000000;
\twhile (s< ((int *)0x30000)) *d++=*s++;
/*
Now jump to the same instruction in the DDR copy of the currently executed code in OCM
Be careful not to call functions or access data stored in the 3 lower OCM pages.
writel() is OK as it is just a macro, not a function call
*/
\tasm("add pc, pc, #0x4000000" );
/*
Remap DDR to zero, FILTERSTART
*/
\twritel(0, &scu_base->filter_start);
/*
Device config APB, unlock the PCAP
*/
\twritel(0x757BDF0D, &devcfg_base->unlock);
\twritel(0xFFFFFFFF, &devcfg_base->rom_shadow);
/*
Now as the code is executed outside of the OCM it is possible to remap the 3 lower
OCM pages to high memory.
OCM_CFG, Mask out the ROM, map ram into upper addresses
*/
\twritel(0x1F, &slcr_base->ocm_cfg);
/*
Copy program memory that we are currently executing to low DRAM (0x0.0x2ffff)
Not possible to call library memcpy() as it will try to access not-yet copied code
*/
\ts= (int *) 0x4000000;
\td= (int *) 0;
\twhile (d < ((int *) 0x30000)) *d++=*s++;
/*
Continue with the original low-level init, Now we have 2 copies of the code again,
currently executing somewhere above 0x4000000. But as soon as we will return
from the call (execute 'bx lr') we'll get back to the low memory.
*/
\t/* FPGA_RST_CTRL, clear resets on AXI fabric ports */
\twritel(0x0, &slcr_base->fpga_rst_ctrl);
\t/* TZ_DDR_RAM, Set DDR trust zone non-secure */
\twritel(0xFFFFFFFF, &slcr_base->trust_zone);
\t/* Set urgent bits with register */
\twritel(0x0, &slcr_base->ddr_urgent_sel);
\t/* Urgent write, ports S2/S3 */
\twritel(0xC, &slcr_base->ddr_urgent);
\tzynq_slcr_lock();
/*
This code was called from low OCM, so return should just get back correctly
*/
}
'''
def output_c_file(self,cname):
if not cname:
return
print 'Writing generated u-boot lowlevel() function to ',cname
c_out_file=open(cname,'w')
c_out_file.write(self.cfile)
c_out_file.close()
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment