@@ -108,8 +108,13 @@ <h1>Tiliqua Flash Tool</h1>
108108 </ div >
109109
110110 < div class ="controls ">
111+ < select id ="bitstream-source " disabled >
112+ < option value ="file "> Upload File</ option >
113+ < option value ="server "> Select from Server</ option >
114+ </ select >
111115 < label for ="archive " class ="disabled " id ="archive-label "> Choose File</ label >
112116 < input type ="file " id ="archive " accept =".tar.gz " disabled >
117+ < select id ="server-bitstreams " class ="disabled " disabled style ="display:none; "> </ select >
113118 < select id ="slot " disabled >
114119 < option value ="null "> Bootloader</ option >
115120 < option value ="0 " selected > Slot 0</ option >
@@ -128,6 +133,7 @@ <h1>Tiliqua Flash Tool</h1>
128133
129134 < script type ="module ">
130135import { runOpenFPGALoader , Exit } from 'https://cdn.jsdelivr.net/npm/@yowasp/openfpgaloader/gen/bundle.js' ;
136+ import { AVAILABLE_BITSTREAMS } from './bitstreams.js' ;
131137
132138// Global state
133139let pyodide = null ;
@@ -257,8 +263,13 @@ <h1>Tiliqua Flash Tool</h1>
257263 statusEl . textContent = `Connected: ${ productName } ` ;
258264 statusEl . style . color = '#4CAF50' ;
259265
266+ // Enable bitstream source selection
267+ document . getElementById ( 'bitstream-source' ) . disabled = false ;
268+
269+ // Enable file upload by default
260270 document . getElementById ( 'archive' ) . disabled = false ;
261271 document . getElementById ( 'archive-label' ) . classList . remove ( 'disabled' ) ;
272+
262273 return tiliquaHwVersion ;
263274
264275 } catch ( error ) {
@@ -281,6 +292,56 @@ <h1>Tiliqua Flash Tool</h1>
281292 }
282293}
283294
295+ function handleBitstreamSourceChange ( ) {
296+ const sourceSelect = document . getElementById ( 'bitstream-source' ) ;
297+ const fileLabel = document . getElementById ( 'archive-label' ) ;
298+ const fileInput = document . getElementById ( 'archive' ) ;
299+ const serverSelect = document . getElementById ( 'server-bitstreams' ) ;
300+ const slotSelect = document . getElementById ( 'slot' ) ;
301+ const flashButton = document . getElementById ( 'flash' ) ;
302+
303+ // Reset selection
304+ selectedFile = null ;
305+ slotSelect . disabled = true ;
306+ flashButton . disabled = true ;
307+
308+ if ( sourceSelect . value === 'file' ) {
309+ // Show file upload, hide server select
310+ fileLabel . style . display = '' ;
311+ fileInput . style . display = '' ;
312+ fileLabel . classList . remove ( 'disabled' ) ;
313+ fileInput . disabled = false ;
314+ serverSelect . style . display = 'none' ;
315+ serverSelect . disabled = true ;
316+ fileLabel . textContent = 'Choose File' ;
317+ } else {
318+ // Show server select, hide file upload
319+ fileLabel . style . display = 'none' ;
320+ fileInput . style . display = 'none' ;
321+ serverSelect . style . display = '' ;
322+ serverSelect . disabled = false ;
323+ serverSelect . classList . remove ( 'disabled' ) ;
324+ populateServerBitstreams ( ) ;
325+ }
326+ }
327+
328+ function populateServerBitstreams ( ) {
329+ const serverSelect = document . getElementById ( 'server-bitstreams' ) ;
330+ serverSelect . innerHTML = '<option value="">Select a bitstream...</option>' ;
331+
332+ for ( const bitstream of AVAILABLE_BITSTREAMS ) {
333+ const option = document . createElement ( 'option' ) ;
334+ option . value = bitstream . url ;
335+ const sizeKB = ( bitstream . size / 1024 ) . toFixed ( 1 ) ;
336+ option . textContent = `${ bitstream . name } (${ sizeKB } KB)` ;
337+ serverSelect . appendChild ( option ) ;
338+ }
339+
340+ if ( AVAILABLE_BITSTREAMS . length === 0 ) {
341+ serverSelect . innerHTML = '<option value="">No bitstreams available</option>' ;
342+ }
343+ }
344+
284345async function handleFileSelect ( event ) {
285346 const file = event . target . files [ 0 ] ;
286347 if ( file ) {
@@ -292,6 +353,36 @@ <h1>Tiliqua Flash Tool</h1>
292353 }
293354}
294355
356+ async function handleServerBitstreamSelect ( event ) {
357+ const url = event . target . value ;
358+ if ( ! url ) {
359+ selectedFile = null ;
360+ document . getElementById ( 'slot' ) . disabled = true ;
361+ document . getElementById ( 'flash' ) . disabled = true ;
362+ return ;
363+ }
364+
365+ try {
366+ log ( `Loading bitstream from server: ${ url } ` ) ;
367+ const response = await fetch ( url ) ;
368+ if ( ! response . ok ) {
369+ throw new Error ( `HTTP ${ response . status } : ${ response . statusText } ` ) ;
370+ }
371+
372+ selectedFile = await response . arrayBuffer ( ) ;
373+ const fileName = url . split ( '/' ) . pop ( ) ;
374+ log ( `Loaded: ${ fileName } (${ selectedFile . byteLength } bytes)` ) ;
375+
376+ document . getElementById ( 'slot' ) . disabled = false ;
377+ document . getElementById ( 'flash' ) . disabled = false ;
378+ } catch ( error ) {
379+ log ( `\x1b[31mFailed to load bitstream: ${ error . message } \x1b[0m` ) ;
380+ selectedFile = null ;
381+ document . getElementById ( 'slot' ) . disabled = true ;
382+ document . getElementById ( 'flash' ) . disabled = true ;
383+ }
384+ }
385+
295386async function handleFlash ( ) {
296387 if ( ! selectedFile || tiliquaHwVersion === null ) {
297388 log ( '\x1b[31mNo file or device\x1b[0m' ) ;
@@ -324,7 +415,20 @@ <h1>Tiliqua Flash Tool</h1>
324415archive_buffer = io.BytesIO(bytes(archive_bytes))
325416files = {}
326417
327- with tarfile.open(fileobj=archive_buffer, mode='r:gz') as tar:
418+ # Try to detect if it's gzipped or plain tar
419+ # Check for gzip magic bytes (0x1f 0x8b)
420+ archive_buffer.seek(0)
421+ first_two = archive_buffer.read(2)
422+ archive_buffer.seek(0)
423+
424+ if first_two == b'\\x1f\\x8b':
425+ # Gzipped tar
426+ tar_mode = 'r:gz'
427+ else:
428+ # Plain tar (might happen if browser/server decompressed it)
429+ tar_mode = 'r'
430+
431+ with tarfile.open(fileobj=archive_buffer, mode=tar_mode) as tar:
328432 for member in tar.getmembers():
329433 if member.isfile():
330434 f = tar.extractfile(member)
@@ -446,7 +550,9 @@ <h1>Tiliqua Flash Tool</h1>
446550 initTerminal ( ) ;
447551 initPyodide ( ) ;
448552 document . getElementById ( 'connect' ) . addEventListener ( 'click' , scanForTiliqua ) ;
553+ document . getElementById ( 'bitstream-source' ) . addEventListener ( 'change' , handleBitstreamSourceChange ) ;
449554 document . getElementById ( 'archive' ) . addEventListener ( 'change' , handleFileSelect ) ;
555+ document . getElementById ( 'server-bitstreams' ) . addEventListener ( 'change' , handleServerBitstreamSelect ) ;
450556 document . getElementById ( 'flash' ) . addEventListener ( 'click' , handleFlash ) ;
451557} ) ;
452558 </ script >
0 commit comments