Teensy 4.0 First Beta Test

Status
Not open for further replies.
Thank you for testing! :)
The Wavetable-examples need a "PROGMEM" before the const tables - I'e done that for Zelda and "Simple", but hear some clicks (with headphones) - don't know what that is. Never tried that on a T3.
Can you try the other wavetable examples and add the missing "PROGMEM" to all of them? (and create a pull-request to the repo?)

PlaySynthMusic works for me without problems.

Updated the repository.
Morning Frank.
Just having my coffee and will go ahead and add PROGMEM and then retest. Will try PlaySythMusic again just in case. Not a problem with testing the examples - there is a lot to audio so have to get it done. :)

Was going to try SD Card play a file today, as soon as I find my extra memory card.

EDIT: Just by way of update I did what you mentioned for Zelda and no dice - wouldn't play. Going to reinstall audio just I in case. I did a copy and replace before so... you never know.
 
Last edited:
Cool, did you try reading as well? That was much slower than writing on the 1050.

Re: 1060 write/read/write

I counted cycles and scoped output pin pulse width when doing write/read/write (sort of like shiftIn())
Code:
  uint32_t t = ARM_DWT_CYCCNT;
  digitalWriteFast(11, HIGH);
  int val = digitalReadFast(12);
  digitalWriteFast(11, LOW);
  t = ARM_DWT_CYCCNT - t;
I tested with variable pin (digitalWrite) and constant pin (WriteFast ReadFast) and with 1060 fast GPIO6 (SDK APIs).
Code:
             variable pin                       constant pin FastWrite/Read
       T3.2 115 cycles         width 700 ns     18 cycles  16.6 ns 2.44v
       T3.6 110                width 350 ns     11 cycles  13.4ns 800 mv
       T4   180 cycles         width 210 ns     75 cycles  120 ns        T4 beta 1050
       [B]1060[/B] 114 cycles               127 ns     49 cycles   74 ns
              [B]fast GPIO6[/B]                        13 cycles   20 ns
Spec for fast GPIO is 150 mhz. For our 3 GPIO operations, 3/150mhz = 20 ns

https://www.nxp.com/docs/en/application-note/AN12240.pdf
 
Last edited:
KurtE - something goes wrong … I was here and looked down and the stars were falling slowly if at all - I restarted and left - and they were stopped on return - without a flicker of the LED.

I'll look to integrate some debug tracing to see what is going on where … first time I change rand of the stars to random(2,4) wondering if it was the math … it was not it seems.
Just went through the code and pulled all the display2 - in no time the stars have fallen still after a great start through all prior screens. The one screen I thought was perhaps trashed is just an odd/unclean inversion.
...

except one missing - it was not powered on 3.3V pin - just feeding off of signal wires … back to the start KurtE test sketch after 101 passes on the prior - and it stopped perhaps a minute after the flakes started … no instrumentation installed for more info. Just this from the SerMon:
Thanks @defragster,

Looks like there may be some form of stress issue and/or timing issue. With the code like:
Code:
    display1.displayAsync(); // Show the display buffer on the screen
    while ( !display1.displayAsync() ) delayMicroseconds( 10 );
    delay(100);        // Pause for 1/10 second

I was sort of lazy and did not test display1, to see if it had completed the previous async update...
Looks like I did not move over a wait for display to complete command yet, but could do something like:

Code:
    display1.displayAsync(); // Show the display buffer on the screen
    while ( !display1.displayAsync() ) delayMicroseconds( 10 );
    while (display1.displayAsyncActive()) ;
    delay(1);
Wondering in this case if issue is with SPI display (display1) or SPI FlexIO (display 2) or with the Asynch support used in display 1 (SPI dma transfers)?

May have to isolate. The dSSD1306... library has one example app that runs the demo code that can be configured to run either displays, by changing a #define.. Note: This test does not do any of the DMA stuff (yet).

Back to playing and/or adding more issues ;)
 
@Frank
No dice - been playing around with Zelda all morning and for some reason those wavetables just don't want to read correctly. I redownloaded audio this morning (saw you updated Zelda) and completely did a reinstall of the library. Still no luck though. Seems to have after a few reads of the score wavetable. Did some prints.
 
@Frank
No dice - been playing around with Zelda all morning and for some reason those wavetables just don't want to read correctly. I redownloaded audio this morning (saw you updated Zelda) and completely did a reinstall of the library. Still no luck though. Seems to have after a few reads of the score wavetable. Did some prints.

Hm.
Don't know. It works for me.

Edit: Does PlaySynthMusic work?
Remember to set the switch on the extension-board correct
smile.png


I'm working on the I2S-input.

@Paul: What shall we do with the Design-Tool ? There are big differences.. 2nd I2S.. MQS, SPDIF-In and more..
a) Copy it and create different versions (T3/T4)
b) Hack the tool and make it more intelligent with a drop-down for the model

I'd choose a)...
 
I killed my audio shield.
Wouldn't be such a problem (have replacement) if I still had long pins...

!"§$%&/

I accidentally put it wrong in the socket after soldering in a microphone. (Pin 26 - Air, Pin 25 -> VIN, Pin 24->AGND...)

@Paul - any chance that the chip is still working and an other part got killed?

Ordered long pins....
 
Last edited:
Thanks @defragster,

Looks like there may be some form of stress issue and/or timing issue. With the code like:
Code:
    display1.displayAsync(); // Show the display buffer on the screen
    while ( !display1.displayAsync() ) delayMicroseconds( 10 );
    delay(100);        // Pause for 1/10 second

I was sort of lazy and did not test display1, to see if it had completed the previous async update...
Looks like I did not move over a wait for display to complete command yet, but could do something like:

Code:
    display1.displayAsync(); // Show the display buffer on the screen
    while ( !display1.displayAsync() ) delayMicroseconds( 10 );
    while (display1.displayAsyncActive()) ;
    delay(1);
Wondering in this case if issue is with SPI display (display1) or SPI FlexIO (display 2) or with the Asynch support used in display 1 (SPI dma transfers)?

May have to isolate. The dSSD1306... library has one example app that runs the demo code that can be configured to run either displays, by changing a #define.. Note: This test does not do any of the DMA stuff (yet).

Back to playing and/or adding more issues ;)

You are welcome - though wish I could be more specific … the non-multi display1 only generally seems to work - and the first thing I did was take off the unused display2 - any error with only display1 may have been my interpretation/reset/restart issue with original delay(100) more than enough time.

If I run this with 500 or fewer in delayus it dies on flakes - taking the time as below and it then recovers and runs clean with proper reset and restart.
Code:
    display1.displayAsync(); // Show the display buffer on the screen
    while ( !display1.displayAsync() ) delayMicroseconds( 10 );
    delayMicroseconds( 600 );
I've stopped the dbgPrint in the flake loop so that wouldn't help it work [given initial hang error showed before prints] - as above it is pretty stable - here is that sketch :: View attachment ssd1306_FlexIO_128x32_spi.ino
It still shows count on outer loop and the _isr is still active I just remembered and at delayus(500) the display SPI on p13_LED and loop stops - but _isr is still active - so you could use that to debug dump ?
You might swap display1 for your FlexIO display1 (without Async) and see how it runs the same - the problem may be there?
 
I killed my audio shield.
Wouldn't be such a problem (have replacement) if I still had long pins...

!"§$%&/

That is !"§$%&/ Frank - dang! I was reading your posts but went with SPI display because it is more familiar to me - and the TeensyView has been on my desk a year. I found long double pins on eBay and have 5 strips of 40 - that doesn't help you now though :(

@KurtE - the code above can run with :: delayMicroseconds( 10 );

The problem is this line in the testLoop() :: display1.display();
Code:
void testLoop()
{
  // Show initial display buffer contents on the screen --
  // the library initializes this with an Adafruit splash screen.
[B]  // display1.display();[/B] // NOW A COMMENT
If that line is moved to the end of setup() the code for display1 runs fast and well - if left in that is where the timing issue comes up!

This is not a problem in your original code as it never did that twice - so it seems the issue is on the other bus. I'll see if I can find the MAP to move the pins and swap

SSD1306_FlexIO_32 display1(OLED_DC, OLED_RESET, OLED_CS, &SPI);
for
SSD1306_FlexIO_32 display1(OLED_DC2, OLED_RESET2, OLED_CS2, &SPIFLEX); // and take off the Async ?
<edit> move to SPIFLEX not coming up as wired pins 2 though 7 … original or edited sketch . . .
 
Last edited:
Thanks @defragster, will play some more later today :D

Right now Trying to understand how to use the DMA with the FlexIO subsystem...

My quick understanding is to use DMA for one of the 4 "channels" like on FlexIO1 instead of ISRs to handle the IO.

That instead of setting the appropriate bit (0-3) in SHIFTSIEN to enable the interrupt for that shifter, you instead set that bit in the SHIFTSDEN
register and obviously setup the DMAChannel information with the correct source and target...

But what I am not sure of is the DMAMUX_SOURCE_ fields... That is if you look at imxrt.h you see:
Code:
#define DMAMUX_SOURCE_FLEXIO1_REQUEST0		0
#define DMAMUX_SOURCE_FLEXIO1_REQUEST1		0
#define DMAMUX_SOURCE_FLEXIO1_REQUEST2		64
#define DMAMUX_SOURCE_FLEXIO1_REQUEST3		64
You will notice that 0-1 have the same value likewise 2-3 have the same number. So the question is, if I wish to use DMA for SPI TX and SPI RX outputs on Shifter 0 and 1, can I use the same DMAMUX_SOURCE value? or is it like the T3.5 where on SPI1 and SPI2, RX and TX were the same and so you could only use that as the source on one of them...

Looks like time to do some searches and to experiment.
 
I killed my audio shield.
Wouldn't be such a problem (have replacement) if I still had long pins...

!"§$%&/

I accidentally put it wrong in the socket after soldering in a microphone. (Pin 26 - Air, Pin 25 -> VIN, Pin 24->AGND...)

@Paul - any chance that the chip is still working and an other part got killed?

Ordered long pins....

On my god Frank. That has been my worst fear. Hopefully you can still get it working on a breadboard - just lots of wires.

As to your other question - didn't just try PlaySynthMusic . Had some errands to run and went back to playing with Flexcan again. Just can't seem to get a signal out to the pins for some strange reason. Will give it a try now
 
...
For FlexIO pin. I setup FlexSPI object on pins (2, 3, 4, -1) (MOSI, MISO, SCLK, CS) These need to be FlexIO pins and on the same FlexIO object (in this case 1:nn) then again CS, DC, RESET can be any digital pin.

KurtE - found a BUG IN CODE …. comment wrong … "// Setup on (int mosiPin, int sckPin, int misoPin, int csPin=-1) :"

The post above is right - MISO and SCLK are swapped in comment and that doesn't fly :)

It seems SPIFLEX is the trouble. It works up to FLAKES then dies - though _isr fires in above non-Multi display1 with swap of constructor and remove the Async for plain display()

Usually makes it to Flakes {scroll okay - but can die on text or diag lines before that} - then when it does dies on flake #3 or [8,11,39] that is with this longer delay after .display:
Code:
FlexSPI SPIFLEX(2, 3, 4, -1); // Setup on (int mosiPin, [B][U]int misoPin, int sckPin[/U][/B], int csPin=-1) :
SSD1306_FlexIO_32 display1(OLED_DC2, OLED_RESET2, OLED_CS2, &SPIFLEX);

//...
    display1.display(); // Show the display buffer on the screen
    //display1.displayAsync(); // Show the display buffer on the screen
    //while ( !display1.displayAsync() ) delayMicroseconds( 10 );
    delayMicroseconds( 1100 );
And again the pin 23 _isr is still active when display stops.
 
News (problems) from the usdhc front
Situation
I can send commands to uSD and also read sectors from the disk
with one caveat:
It seems that on read I need in addition to 128 uint32 accessed to data port an extra read, which seems to be some sort of CRC
I say some sort, as it does NOT correspond to the documented CRC
e.g. reading the MBR (record 0) the additional word reads 0xE8E8E8E8 while the CRC is F01F

Update:
The additional word I was reading/writing was due to wrong Block Attribute register.

Still have problems with write….

if I write, say the MBR to sector 1 (which is unused and does not destroy the file system)
the write is not executed by the uSD except I add an additional write of 0xE8E8E8E8 after the 128 write accesses to the data port (512 bytes)
writing any other word fails, that is uSD does not write the data to disk.

No idea hoe to calculate the additional 32 bit word (which seems to depend on block data but is always 4 equal bytes)

(uSD disk is 32 GB Transcendent FAT32 formatted and using/modifying the stock SD library)

or to eliminate this additional word

NOTE: for K66 this word does not show up on read and is not required on write

Edit: I know
When the wide bus option is used, the data is transferred 4 bits at a time. Start and end bits, as well as the CRC bits, are transmitted for every one of the DAT lines. CRC bits are calculated and checked for every DAT line individually.
that is, the CRC estimation is somewhat complex and I would like to avoid it, I could not find any hint on hoe to tell system to ignore CRC.
(it seems no issue with K66)
 
Last edited by a moderator:
Hm.
Don't know. It works for me.

Edit: Does PlaySynthMusic work?
Remember to set the switch on the extension-board correct
smile.png


I'm working on the I2S-input.

@Paul: What shall we do with the Design-Tool ? There are big differences.. 2nd I2S.. MQS, SPDIF-In and more..
a) Copy it and create different versions (T3/T4)
b) Hack the tool and make it more intelligent with a drop-down for the model

I'd choose a)...

@Frank
Just tried PlyaSynthMusic and it does not work - had the switch set to i2s1. The thing I noticed is that with the wavetable (progmem) examples when the sketch stops the LED starts blinking until I load another sketch or reset. Also anything other than using SGT5000/i2s1 as output does not work either, i.e., codec, audioout etc. Have to do some debugging on progmem later - have to go out now.

Mike
 
KurtE - found a BUG IN CODE …. comment wrong … "// Setup on (int mosiPin, int sckPin, int misoPin, int csPin=-1) :"

The post above is right - MISO and SCLK are swapped in comment and that doesn't fly :)

It seems SPIFLEX is the trouble. It works up to FLAKES then dies - though _isr fires in above non-Multi display1 with swap of constructor and remove the Async for plain display()

Usually makes it to Flakes {scroll okay - but can die on text or diag lines before that} - then when it does dies on flake #3 or [8,11,39] that is with this longer delay after .display:
Code:
FlexSPI SPIFLEX(2, 3, 4, -1); // Setup on (int mosiPin, [B][U]int misoPin, int sckPin[/U][/B], int csPin=-1) :
SSD1306_FlexIO_32 display1(OLED_DC2, OLED_RESET2, OLED_CS2, &SPIFLEX);

//...
    display1.display(); // Show the display buffer on the screen
    //display1.displayAsync(); // Show the display buffer on the screen
    //while ( !display1.displayAsync() ) delayMicroseconds( 10 );
    delayMicroseconds( 1100 );
And again the pin 23 _isr is still active when display stops.

Sorry, I meant to update the comment. As noted in the posting I had the pin numbers and caught it when I was rehooking mine back up, but forgot to update comment :0

Looks like some of the Flex stuff needs some more debugging, especially when used with other things.

Wondering is it hanging or just not updating properly? If hanging, maybe need to setup some form of timeout and try to figure out what states are at the time things are not working...

If not updating properly, maybe need to grab state at startup of each display attempt and try to see if some state values are not correct...

Edit: comment on the example app on github was updated
 
@Paul- using external editor with KurtE's long samples - not seen the Ctrl+U delay. So it is IDE file system use - really weird on File/Open Recent - and that @tonton81 sees it too.
I didn't restart IDE and Teensy tools yet so T4 Teensy port still missing - so using IDE SerMon and have to do Ctrl+U because button press upload leaves IDE_SerMon closed and orphaned until close/open

@KurtE - removed Flakes from testLoop() and shortened scroll delays/10 (hasn't died in scroll yet) - it will complete a couple of loops but dies there too after 0,1,3,4 or 6 passes in one of the drawings - so SPIFLEX fail not unique to Flakes. And to be clear - the loop() stops or it would be showing completed loops - not just an output failure - but interesting the _isr still active and not faulted. So it seems to be stuck in some loop in SPIFLEX code. I could probably find where with debug_tt ...
 
@Frank
Just tried PlyaSynthMusic and it does not work - had the switch set to i2s1. The thing I noticed is that with the wavetable (progmem) examples when the sketch stops the LED starts blinking until I load another sketch or reset. Also anything other than using SGT5000/i2s1 as output does not work either, i.e., codec, audioout etc. Have to do some debugging on progmem later - have to go out now.

Mike

Well, there is no other explanation than a different installation :)
We'll find it..
 
@KurtE - removed Flakes from testLoop() and shortened scroll delays/10 (hasn't died in scroll yet) - it will complete a couple of loops but dies there too after 0,1,3,4 or 6 passes in one of the drawings - so SPIFLEX fail not unique to Flakes. And to be clear - the loop() stops or it would be showing completed loops - not just an output failure - but interesting the _isr still active and not faulted. So it seems to be stuck in some loop in SPIFLEX code. I could probably find where with debug_tt ...
Thought I would try to shorten the test to just run the scrolling flakes... So after the initial screen begin and erase, it then just called testanimate...

I changed the code to also output a loop counter on the displays and shortened the delay to 10...

The function now looks like:
Code:
void testanimate(const uint8_t *bitmap, uint8_t w, uint8_t h) {
  int8_t f, icons[NUMFLAKES][3];

  // Initialize 'snowflake' positions
  for (f = 0; f < NUMFLAKES; f++) {
    icons[f][XPOS]   = random(1 - LOGO_WIDTH, display1.width());
    icons[f][YPOS]   = -LOGO_HEIGHT;
    icons[f][DELTAY] = random(1, 6);
    Serial.print(F("x: "));
    Serial.print(icons[f][XPOS], DEC);
    Serial.print(F(" y: "));
    Serial.print(icons[f][YPOS], DEC);
    Serial.print(F(" dy: "));
    Serial.println(icons[f][DELTAY], DEC);
  }

  uint32_t loop_count= 0;
  display1.setTextSize(1);      // Normal 1:1 pixel scale
  display1.setTextColor(WHITE); // Draw white text
  display2.setTextSize(1);      // Normal 1:1 pixel scale
  display2.setTextColor(WHITE); // Draw white text
  for (;;) { // Loop forever...
    display1.clearDisplay(); // Clear the display buffer
    display2.clearDisplay(); // Clear the display buffer

    // Draw each snowflake:
    for (f = 0; f < NUMFLAKES; f++) {
      display1.drawBitmap(icons[f][XPOS], icons[f][YPOS], bitmap, w, h, WHITE);
      display2.drawBitmap(icons[f][XPOS], icons[f][YPOS], bitmap, w, h, WHITE);
    }
    loop_count++;
    display1.setCursor(0, 0);     // Start at top-left corner
    display1.print(loop_count,DEC);
    display2.setCursor(0, 0);     // Start at top-left corner
    display2.print(loop_count,DEC);
    
    display1.displayAsync(); // Show the display buffer on the screen
    display2.display(); // Show the display buffer on the screen
    
    while (display1.displayAsyncActive()) ;
    delay(10);        // Pause for 1/10 second

    // Then update coordinates of each flake...
    for (f = 0; f < NUMFLAKES; f++) {
      icons[f][YPOS] += icons[f][DELTAY];
      // If snowflake is off the bottom of the screen...
      if (icons[f][YPOS] >= display1.height()) {
        // Reinitialize to a random position, just off the top
        icons[f][XPOS]   = random(1 - LOGO_WIDTH, display1.width());
        icons[f][YPOS]   = -LOGO_HEIGHT;
        icons[f][DELTAY] = random(1, 6);
      }
    }
  }
}
It now has been running for maybe an hour or two, and the counters are showing over 395K loops, still running...

Do you have an example one that hangs for you? might help to start from there to figure out why.
 
Do you have an example one that hangs for you? might help to start from there to figure out why.

May not be relevant, but a few days ago I had a flexspi hang problem at high speed
https://forum.pjrc.com/threads/54711-Teensy-4-0-First-Beta-Test?p=196848&viewfull=1#post196848

? SPI@30mhz block transfer ran twice and hung ?
In flexSPI.cpp, if i change transfer() while to while (tx_count) instead of rx_count, sketch doesn't hang @30mhz (26.7 mbs), no inter-frame gaps
That "fix" may be just "covering the warning light" :D
 
May not be relevant, but a few days ago I had a flexspi hang problem at high speed
https://forum.pjrc.com/threads/54711-Teensy-4-0-First-Beta-Test?p=196848&viewfull=1#post196848


That "fix" may be just "covering the warning light" :D
Sounds like it must have missed one or more interrupts. That is if there is a delay of time before it services a TX request ISR, the SPI output will have a gap in time. If however it waits too long to service an Receive interrupt and another byte comes in than we miss an interrupt (and character) and the code waits for a very long time (for ever) for the remaining byte to come.

Sounds like probably need to have some form of timeout here... May also need to do more efficient ISR handling. Although I tried to short cut some of this stuff...
 
Well, there is no other explanation than a different installation :)
We'll find it..

@Frank
Just as a quick and dirty test of PROGMEM I created a quick sketch to read the score array and then print it out. It worked without an problem, but I did have to delete the "const" before the array for the test since it caused an error in types with the pointer. I did the same with the example, it seemed to read the data correctly this time but then hung the T4 (little red light kept blinking when it hung). reloading a good sketch resolved that problem.

Not sure where the problem is. Like you said we will figure it out.
 
Thought I would try to shorten the test to just run the scrolling flakes... So after the initial screen begin and erase, it then just called testanimate...

I changed the code to also output a loop counter on the displays and shortened the delay to 10...

The function now looks like:
// ...
It now has been running for maybe an hour or two, and the counters are showing over 395K loops, still running...

Do you have an example one that hangs for you? might help to start from there to figure out why.

I put your Flake code in place of mine and removed display2 where my display 1 is on FlexSPI. Only running Flakes ...

I got runs of ~600, ~200 and the last 4257. Moved loop_count global and prints from the _isr button to avoid mag lens to read: 828, 37 and 908 are the last three counts.

That updated non-Multi sketch is ::
View attachment ssd1306_FlexIO_128x32_spi.ino
// SSD1306_FlexIO_32 display1(OLED_DC2, OLED_RESET2, OLED_CS2, &SPIFLEX);
and _isr button on pin #23

>> Using crummy molded end jumpers wires - though short - on new EleGoo breadboard. Not crossing the Serial# lines and just passing T4 to TView. Last runs: 1092, 4544, 868.
TView is uncut&unsoldered on under OLED jumpers. Though I did minimal solder only on used pins so I could remove if desired. Did a brief look for 2nd TView - but not in the right spot to find it :(
 
Last edited:
I don't have any problems - but I'm using TY.

I've been using SerMon on T4 - and TyComm on the two debug serial Teensy 3.1's - and T4 not showing on T_ports meant using the SerMon port version.

Reading this post made me decide to use TyComm as sermon for T4 - I have to manually handle Serial enable to free the T4 to program.

@KURTE! since doing that I got one run of latest TView FlexSPI at 10890 before I stopped it, and the current run is at 24,512 and counting the flying Flakes.

I'm going to bail on SerMon and TLOader and try the Integrated TyComm_beta_T4 code. It seems something in the IDE USB is my problem - not hopefully what I thought was TyComm.

NOTE: This [AdaFruit] KurtE sketch only dumps to USB 11 lines on startup - so it isn't even active.

OLED Flakes cnt was 44159 - but pushing the button was followed by a break so restarted and it just passed 7,000 no problem - before my max was 6.

<edit> Beta TyComm Integrated and it is running free of IDE SerMon. Since restarting the IDE - it now again shows all three Teensy's under:: Port / Teensy

First TyComm upload failed to run - but ran on GUI RESET. Next Upload is fine at 25+K Flake cycles

Also note USB is online in FASTER with TyComm - it was close to 2000 millis again when Serial is there at 305ms. With TyComm USB online at 383ms!

Flakes do fail on some starts with low counts - suspect it is the issue I was seeing in testing micros() where there was some soon after loop() entry CPU hit that made the test miss a microsecond count. As noted - am hoping/expecting all this to work out when new USB code comes to T4.

KurtE - Flakes is FINE again at 34K and counting ! It seems the FlexSPI is fine, when it doesn't get a timing hit?
 
Last edited:
@defragster - Running your version of the sketch now...

In order to make it easier to read without magnifier, I changed setTextSize(2) instead of 1 ;)

I just screwed up and was thinking I was plugging in my USB to Serial adapter to my PC, but instead to wall wart (power for RPI3) and for some reason the Adafruit board started to show magic smoke... So now I am going into my parts and grabbing a different converter. Maybe Sparkfun FTDI Basic 3.3v version...

I had your version hang once... Did not have button hooked up nor other serial port... Current run > 88000

Need to setup the sparkfun hunit... Not sure if I will put on Serial1 or Serial4...
 
Status
Not open for further replies.
Back
Top