From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751608AbdBOTtX (ORCPT ); Wed, 15 Feb 2017 14:49:23 -0500 Received: from quartz.orcorp.ca ([184.70.90.242]:58939 "EHLO quartz.orcorp.ca" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751313AbdBOTtV (ORCPT ); Wed, 15 Feb 2017 14:49:21 -0500 Date: Wed, 15 Feb 2017 12:49:18 -0700 From: Jason Gunthorpe To: Alan Tull Cc: Moritz Fischer , linux-kernel , linux-fpga@vger.kernel.org Subject: Re: [RFC 7/8] fpga-region: add sysfs interface Message-ID: <20170215194918.GA5531@obsidianresearch.com> References: <1487175261-7051-1-git-send-email-atull@kernel.org> <1487175261-7051-8-git-send-email-atull@kernel.org> <20170215172157.GA3317@obsidianresearch.com> <20170215180612.GB3317@obsidianresearch.com> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: User-Agent: Mutt/1.5.24 (2015-08-30) X-Broken-Reverse-DNS: no host name found for IP address 10.0.0.156 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On Wed, Feb 15, 2017 at 12:23:28PM -0600, Alan Tull wrote: > > This is usually the sort of stuff I'd punt to userspace, but since the > > kernel is doing request_firmware it is hard to see how that is an > > option in this case... > > I like how extensible (and readable!) this is. It wouldn't take much > kernel code to add this. I'd like to see the python script. Sure, attached Jason #!/usr/bin/env python # COPYRIGHT (c) 2016 Obsidian Research Corporation. # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. import subprocess,re,string,os,stat,mmap,sys,base64; import argparse import time; def timeRFC1123(): # Python doesn't have this as a built in return subprocess.check_output(["date","-u",'+%a, %02d %b %Y %T GMT']).strip(); def getTOT(): """Return the top of tree commit hash from git""" try: HEAD = subprocess.check_output(["git","rev-parse","--verify","HEAD"]).strip(); except subprocess.CalledProcessError: return "??? %s"%(os.getcwd()); dirty = subprocess.check_output(["git","diff","--stat"]).strip(); if dirty: return "%s-dirty"%(HEAD); return HEAD; def parseISEPar(f,fmts): """Read Xilinx ISE par log files to get relevant design information""" f = string.join(f.readlines()); g = re.search('"([^"]+)" is an NCD, version [0-9.]+, device (\S+), package (\S+), speed -(\d+)',f).groups(); fmts.update({"Design": g[0], "Device": g[1], "Package": g[2], "Speed": g[3]}); fmts["PAR-Ver"] = re.search("(Release \S+ par \S+(?: \(\S+\))?)\n",f).groups()[0] fmts["Speed-File"] = re.search('Device speed data version:\s+"([^"]+)"',f).groups()[0]; def parseVivadoTwr(f,fmts): """Read Vivado 'report_timing' log files to get relevant design information""" f = string.join(f.readlines(1024)); g = re.search('Design\s+:\s+(\S+)',f).groups(); fmts["Design"] = g[0]; g = re.search('Speed File\s+:\s+-(\d+)\s+(.+)',f); if g is not None: g = g.groups(); fmts["Speed"] = g[0]; fmts["Speed-File"] = g[1]; g = re.search('Device\s+:\s+(\S+)-(\S+)',f).groups(); fmts["Device"] = g[0]; fmts["Package"] = g[1]; else: g = re.search('Part\s+:\s+Device=(\S+)\s+Package=(\S+)\s+Speed=-(\d+)\s+\((.+)\)',f).groups(); fmts.update({"Device": g[0], "Package": g[1], "Speed": g[2], "Speed-File": g[3]}); g = re.search('Version\s+:\s+(.+)',f).groups(); fmts["Xilinx-Ver"] = g[0]; def parseSrr(f,fmts): """Read Synplify log files to get relevent design information""" l = f.readline().strip(); fmts["Synplify-Ver"] = re.match("#Build: Synplify (.*)",l).groups()[0]; def find_start(bitm): """Locate the start of the actual bitsream in a Xilinx .bit file. Xilinx tools drop an Impact header in front of the sync word. The FPGA ignores everything prior to the sync word.""" for I in range(len(bitm)): if (bitm[I] == '\xaa' and bitm[I+1] == '\x99' and bitm[I+2] == '\x55' and bitm[I+3] == '\x66'): return I; return 0; def align_bitstream(fmts,alignment=8): """Adjust the header content so that the bitstream starts aligned. This is so we can mmap this file with the header and still DMA from it.""" while True: hdr = ("YYBIT/1.0\n" + "\n".join("%s: %s"%(k,v) for k,v in sorted(fmts.iteritems())) + "\n\n"); if len(hdr) % alignment == 0: return hdr; fmts["Pad"] = "x"*(alignment - ((len(hdr) + 6) % alignment)); def makeHeader(out,args): fmts = { "Builder": os.getenv("USERNAME",os.getenv("USER","???")), "Date": timeRFC1123(), "GIT-TOT": getTOT(), "Bit-Order": args.order, }; for fn in args.logs: with open(fn) as F: if fn.endswith(".par"): parseISEPar(F,fmts); if fn.endswith(".srr"): parseSrr(F,fmts); if fn.endswith(".twr"): parseVivadoTwr(F,fmts); if fn.endswith(".tsr"): parseVivadoTwr(F,fmts); with open(args.bit) as bitf: bitlen = os.fstat(bitf.fileno())[stat.ST_SIZE]; bitm = mmap.mmap(bitf.fileno(),bitlen,access=mmap.ACCESS_COPY); start = 0; # This is the format for our bit bang schemes. The pin labeled D0 is # taken from bit 7. if args.order == "reversed": for i in range(0,bitlen): v = ord(bitm[i]); bitm[i] = chr(((v & (1<<0)) << 7) | ((v & (1<<1)) << 5) | ((v & (1<<2)) << 3) | ((v & (1<<3)) << 1) | ((v & (1<<4)) >> 1) | ((v & (1<<5)) >> 3) | ((v & (1<<6)) >> 5) | ((v & (1<<7)) >> 7)); # This is the format DMA to devcfg on the Zynq wants, impact header # stripped, sync word in little endian and aligned. if args.order == "byte-reversed": start = find_start(bitm); for i in range(start,bitlen//4*4,4): bitm[i],bitm[i+1],bitm[i+2],bitm[i+3] = bitm[i+3],bitm[i+2],bitm[i+1],bitm[i]; if start != 0: fmts["Impact-Header"] = base64.b64encode(bitm[:start]); fmts["Content-Length"] = bitlen - start; out.write(align_bitstream(fmts)); out.write(bitm[start:]); parser = argparse.ArgumentParser(description="Format a Xilinx .bit file into a ybf with the necessary headers") parser.add_argument("--ybf",required=True, help="Output filename"); parser.add_argument("--bit",required=True, help="Input bit filename"); parser.add_argument("--archive", help="Optional directory to place a timestamped hardlink"); parser.add_argument("--deps", help="File to write makefile dependencies list to"); parser.add_argument("--order",default="reversed", help="Byte or bit order to use for the raw data"); parser.add_argument('logs',nargs="+", help="Log files to pull meta data out of") args = parser.parse_args(); with open(args.ybf,"wt") as F: makeHeader(F,args); if args.archive: os.link(args.ybf,os.path.join(args.archive,"%s-%s"%(os.path.basename(args.ybf),int(time.time()))));