x393_mem.py 14.9 KB
Newer Older
1
from __future__ import print_function
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
'''
# Copyright (C) 2015, Elphel.inc.
# Memory read/write functions 
# 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:  2015 Elphel, Inc.
@license:    GPLv3.0+
@contact:    andrey@elphel.coml
@deffield    updated: Updated
'''
__author__ = "Andrey Filippov"
__copyright__ = "Copyright 2015, Elphel, Inc."
__license__ = "GPL"
__version__ = "3.0+"
__maintainer__ = "Andrey Filippov"
__email__ = "andrey@elphel.com"
__status__ = "Development"
import mmap
#import sys
import struct
34
import os
35 36 37 38
class X393Mem(object):
    '''
    classdocs
    '''
39
    DRY_MODE= True # True
40 41 42
    PAGE_SIZE=4096
    DEBUG_MODE=1
    ENDIAN="<" # little, ">" for big
43 44 45 46 47
    MAXI0_BASE=0x40000000
    MAXI1_BASE=0x80000000
    MAXI_BASE = MAXI0_BASE
    USE_NEGATIVE = True # Old versions of Python (2.7.3) required mmap offset to fit into 32 bits, so upper half had to be negative
                        # 2.7.11 does not need subtraction(and reports error if negative)
48

49 50 51 52 53
    def __init__(self, debug_mode=1,dry_mode=False, maxi_port=0):
        if maxi_port:
            self.MAXI_BASE=self.MAXI1_BASE
        else:    
            self.MAXI_BASE=self.MAXI0_BASE;
54
        self.DEBUG_MODE=debug_mode
55 56 57 58
        if not dry_mode:
            if not os.path.exists("/dev/xdevcfg"):
                dry_mode=True
                print("Program is forced to run in SIMULATED mode as '/dev/xdevcfg' does not exist (not a camera)")
59
        self.DRY_MODE=dry_mode
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
    def maxi_base(self, maxi_port=None):
        if not maxi_port is None:
            if maxi_port:
                self.MAXI_BASE=self.MAXI1_BASE
            else:    
                self.MAXI_BASE=self.MAXI0_BASE;
        else:
            maxi_port = (0,1)[self.MAXI_BASE == self.MAXI1_BASE]
        print ("MAXI port = %d (0x%08x)"%(maxi_port, self.MAXI_BASE))       

    def wrap_mm (self, f, page_addr):
        if page_addr>=0x80000000:
            if (self.USE_NEGATIVE):
                try:
                    return mmap.mmap(f.fileno(), self.PAGE_SIZE, offset = page_addr - (1<<32))
                except:
                    self.USE_NEGATIVE = False
                    if self.DEBUG_MODE:
                        print ("Turning OFF use of negative offsets in mmap")
                    return mmap.mmap(f.fileno(), self.PAGE_SIZE, offset = page_addr)
            else:
                try:
                    return mmap.mmap(f.fileno(), self.PAGE_SIZE, offset = page_addr)
                except:
                    print ("Turning ON use of negative offsets in mmap")
                    self.USE_NEGATIVE = True
                    return mmap.mmap(f.fileno(), self.PAGE_SIZE, offset = page_addr - (1<<32))
        else:
             return mmap.mmap(f.fileno(), self.PAGE_SIZE, offset = page_addr)
         
                        
91
    def write_mem (self,addr, data,quiet=1):
92 93
        """
        Write 32-bit word to physical memory
94 95 96
        @param addr - physical byte address
        @param data - 32-bit data to write
        @param quiet - reduce output
97
        """
98
        if self.DRY_MODE:
99
            print ("simulated: write_mem(0x%x,0x%x)"%(addr,data))
100 101 102 103
            return
        with open("/dev/mem", "r+b") as f:
            page_addr=addr & (~(self.PAGE_SIZE-1))
            page_offs=addr-page_addr
104
            mm = self.wrap_mm(f, page_addr)
105 106
#            if (page_addr>=0x80000000):
#                page_addr-= (1<<32)
107
#            mm = mmap.mmap(f.fileno(), self.PAGE_SIZE, offset=page_addr)
108 109 110
            packedData=struct.pack(self.ENDIAN+"L",data)
            d=struct.unpack(self.ENDIAN+"L",packedData)[0]
            mm[page_offs:page_offs+4]=packedData
111
            if quiet <1:
112
                print ("0x%08x <== 0x%08x (%d)"%(addr,d,d))
113 114 115 116 117 118 119 120 121 122
        '''    
        if MONITOR_EMIO and VEBOSE:
            gpio0=read_mem (0xe000a068)
            gpio1=read_mem (0xe000a06c)
            print("GPIO: %04x %04x %04x %04x"%(gpio1>>16, gpio1 & 0xffff, gpio0>>16, gpio0 & 0xffff))
            if ((gpio0 & 0xc) != 0xc) or ((gpio0 & 0xff00) != 0):
                print("******** AXI STUCK ************")
                exit (0)
        '''    

123
    def read_mem (self,addr,quiet=1):
124
        '''
125 126 127
        Read 32-bit word from physical memory
        @param addr  physical byte address
        @param quiet - reduce output
128
        '''    
129
        if self.DRY_MODE:
130
            print ("simulated: read_mem(0x%x)"%(addr))
131
            return addr # just some data
132 133 134
        with open("/dev/mem", "r+b") as f:
            page_addr=addr & (~(self.PAGE_SIZE-1))
            page_offs=addr-page_addr
135
            mm = self.wrap_mm(f, page_addr)
136 137
#            if (page_addr>=0x80000000):
#                page_addr-= (1<<32)
138
#            mm = mmap.mmap(f.fileno(), self.PAGE_SIZE, offset=page_addr)
139 140
            data=struct.unpack(self.ENDIAN+"L",mm[page_offs:page_offs+4])
            d=data[0]
141
            if quiet < 1:
142 143
                print ("0x%08x ==> 0x%08x (%d)"%(addr,d,d))
            return d
144 145

    def mem_dump (self, start_addr, end_addr=1, byte_mode=4):
146 147
        '''
         Read and print memory range from physical memory
148 149 150 151
         @param start_addr physical byte start address
         @param end_addr  physical byte end address (inclusive), if negative/less than start_addr - number of items
         @param byte_mode number of bytes per item (1,2,4,8)
         @return list of read values
152
        '''
153 154 155 156 157 158 159 160 161 162 163 164 165 166
        frmt_bytes={1:'B',2:'H',4:'L',8:'Q'}
        bytes_per_line_mask={1:0x1f,2:0x1f,4:0x3f,8:0x3f}
        default_byte_mode=4
        if not byte_mode in frmt_bytes.keys():
            print ("Invalid byte mode: '%s'. Only %s are supported. Using %d"%(str(byte_mode),str(frmt_bytes.keys()),default_byte_mode))
            byte_mode=default_byte_mode
        data_frmt=  "%%0%dx"%(2*byte_mode)
        simul_mask= (1 << (8*byte_mode)) -1
        addr_mask=0xffffffff ^ (byte_mode-1)
        start_addr &= addr_mask
        if end_addr < start_addr:
            end_addr=start_addr + abs(end_addr*byte_mode) -1
        end_addr  &= addr_mask
#       align start address to 32-bit word even if the mode is byte/short        
167
        start_addr &= 0xfffffffc
168
        print_mask=bytes_per_line_mask[byte_mode]
169 170
        rslt=[]
        if self.DRY_MODE:
171
            rslt=[d & simul_mask for d in range(start_addr,end_addr+byte_mode,byte_mode)]
172 173
        else:
            with open("/dev/mem", "r+b") as f:
174
                for addr in range (start_addr,end_addr+byte_mode,byte_mode):
175 176
                    page_addr=addr & (~(self.PAGE_SIZE-1))
                    page_offs=addr-page_addr
177 178 179 180
                    mm = self.wrap_mm(f, page_addr)
        #            if (page_addr>=0x80000000):
        #                page_addr-= (1<<32)
        #            mm = mmap.mmap(f.fileno(), self.PAGE_SIZE, offset=page_addr)
181
                    data=struct.unpack_from(self.ENDIAN+frmt_bytes[byte_mode],mm, page_offs)
182 183
                    rslt.append(data[0])
                    
184 185
        for addr in range (start_addr,end_addr+byte_mode,byte_mode):
            if (addr == start_addr) or ((addr & print_mask) == 0):
186 187 188 189
                if self.DRY_MODE:
                    print ("\nsimulated: 0x%08x:"%addr,end="")
                else:     
                    print ("\n0x%08x:"%addr,end="")
190 191
            d=rslt[(addr-start_addr) // byte_mode]
            print (data_frmt%(d),end=" ")
192 193
        print("")
        return rslt    
194

195 196 197 198 199 200 201 202 203 204
    def mem_save (self, filename, start_addr, length):
        '''
         Save physical memory content to a file
         @param filename - path to a file
         @param start_addr physical byte start address
         @param length - number of bytes to save
        '''
        if self.DRY_MODE:
            print ("Write memory to file is not implemented in non-target mode")
            return
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230
        with open(filename, "w+b") as bf:
            self._mem_write_to_file (bf =         bf,
                                     start_addr = start_addr,
                                     length =     length)

    def _mem_write_to_file (self, bf, start_addr, length):
        '''
         Save physical memory content to a file
         @param bf - file open in write mode
         @param start_addr physical byte start address
         @param length - number of bytes to save
        '''
        if self.DRY_MODE:
            print ("Write memory to file is not implemented in non-target mode")
            return
        with open("/dev/mem", "r+b") as f:
            first_page = start_addr // self.PAGE_SIZE
            last_page = (start_addr + length - 1) // self.PAGE_SIZE
            for page_num in range(first_page, last_page+1):
                start_offset = 0
                if page_num == first_page:
                    start_offset = start_addr - self.PAGE_SIZE * page_num
                end_offset =  self.PAGE_SIZE
                if page_num == last_page:
                    end_offset = start_addr + length - self.PAGE_SIZE * page_num
                page_addr = page_num * self.PAGE_SIZE 
231 232 233 234
                mm = self.wrap_mm(f, page_addr)
    #            if (page_addr>=0x80000000):
    #                page_addr-= (1<<32)
    #            mm = mmap.mmap(f.fileno(), self.PAGE_SIZE, offset=page_addr)
235
                bf.write(mm[start_offset:end_offset])
236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258

    def mem_clear (self, start_addr, length, word32):
        '''
         Fill physical memory range with a repetitive 32-bit word
         @param start_addr physical byte start address
         @param length - number of bytes to save
         @param word32 - 32-bit pattern
        '''
        if self.DRY_MODE:
            print ("Write memory to file is not implemented in non-target mode")
            return
        patt=str(bytearray(((word32 & 0xff), ((word32 >> 8) & 0xff), ((word32 >> 16) & 0xff), ((word32 >> 24) & 0xff)))*(self.PAGE_SIZE/4))
        with open("/dev/mem", "r+b") as f:
            first_page = start_addr // self.PAGE_SIZE
            last_page = (start_addr + length - 1) // self.PAGE_SIZE
            for page_num in range(first_page, last_page+1):
                start_offset = 0
                if page_num == first_page:
                    start_offset = start_addr - self.PAGE_SIZE * page_num
                end_offset =  self.PAGE_SIZE
                if page_num == last_page:
                    end_offset = start_addr + length - self.PAGE_SIZE * page_num
                page_addr = page_num * self.PAGE_SIZE 
259 260 261 262
                mm = self.wrap_mm(f, page_addr)
    #            if (page_addr>=0x80000000):
    #                page_addr-= (1<<32)
    #            mm = mmap.mmap(f.fileno(), self.PAGE_SIZE, offset=page_addr)
263 264 265
                mm[start_offset:end_offset] = patt[start_offset:end_offset]


266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294
    def mem_fill (self, start_addr, start_data=0, end_addr=1, inc_data=0, byte_mode=4):
        '''
         Read and print memory range from physical memory
         @param start_addr physical byte start address
         @param start_data data/start data to write
         @param end_addr  physical byte end address (inclusive), if negative/less than start_addr - number of items
         @param inc_data increment each next item by this value
         @param byte_mode number of bytes per item (1,2,4,8)
        '''
        frmt_bytes={1:'B',2:'H',4:'L',8:'Q'}
        default_byte_mode=4
        if not byte_mode in frmt_bytes.keys():
            print ("Invalid byte mode: '%s'. Only %s are supported. Using %d"%(str(byte_mode),str(frmt_bytes.keys()),default_byte_mode))
            byte_mode=default_byte_mode
        data_mask= (1 << (8*byte_mode)) -1
        addr_mask=0xffffffff ^ (byte_mode-1)
        start_addr &= addr_mask
        if end_addr < start_addr:
            end_addr=start_addr + abs(end_addr*byte_mode) -1
        end_addr  &= addr_mask
#       align start address to 32-bit word even if the mode is byte/short        
        start_addr &= 0xfffffffc
        if self.DRY_MODE:
            print ("Simulated mem_fill(0x%x, 0x%x, 0x%x, 0x%x, %d)"%(start_addr, start_data, end_addr, inc_data, byte_mode))
            data_frmt=  "%%0%dx"%(2*byte_mode)
            for addr in range (start_addr,end_addr+byte_mode,byte_mode):
                data = (start_data + ((addr-start_addr) // byte_mode)*inc_data) & data_mask
                page_addr=addr & (~(self.PAGE_SIZE-1))
                page_offs=addr-page_addr
295 296
                if (page_addr>=0x80000000):
                    page_addr-= (1<<32)
297 298 299 300 301 302 303
                print (("0x%08x: "+ data_frmt)%(addr,data))
        else:
            with open("/dev/mem", "r+b") as f:
                for addr in range (start_addr,end_addr+byte_mode,byte_mode):
                    data = (start_data + ((addr-start_addr) // byte_mode)*inc_data) & data_mask
                    page_addr=addr & (~(self.PAGE_SIZE-1))
                    page_offs=addr-page_addr
304 305 306 307
                    mm = self.wrap_mm(f, page_addr)
        #            if (page_addr>=0x80000000):
        #                page_addr-= (1<<32)
        #            mm = mmap.mmap(f.fileno(), self.PAGE_SIZE, offset=page_addr)
308 309
                    struct.pack_into(self.ENDIAN+frmt_bytes[byte_mode],mm, page_offs, data)
    
310
    '''
311
    Read/write slave AXI using byte addresses relative to the AXI memory region
312 313
    '''
    def axi_write_single(self,addr,data):
314 315 316 317 318
        """
        Write 32-bit word to the slave AXI address range
        <addr> - physical byte address relative to the slave AXI memory region
        <data> - 32-bit data to write
        """
319
        self.write_mem(self.MAXI_BASE+addr,data)
320 321

    def axi_read_addr(self,addr):
322 323 324 325
        """
        Read 32-bit word from the  slave AXI address range
        <addr> - physical byte address relative to slave AXI AXI memory region
        """
326
        return self.read_mem(self.MAXI_BASE+addr)
327 328 329
    '''
    Read/write slave AXI using 32-bit word addresses (same as in Verilog code)
    '''
330
    def axi_write_single_w(self,addr,data,verbose=0):
331 332 333 334
        """
        Write 32-bit word to the slave AXI address range, using 32-word address
        <addr> - 32-bit word (register) address relative to the slave AXI memory region
        <data> - 32-bit data to write
335
        <verbose> print data being written (default: 0)
336
        """
337
        if verbose or self.DEBUG_MODE:
338
            print("axi_write_single_w(0x%x,0x%08x)"%(addr,data))
339 340 341
        self.axi_write_single(addr<<2,data)

    def axi_read_addr_w(self,addr):
342 343 344 345
        """
        Read 32-bit word from the slave AXI address range, using 32-word address
        <addr> - 32-bit word (register) address relative to the slave AXI memory region
        """
346
        return self.axi_read_addr(addr<<2)