Vidi Project X #89:
Višejezgrena okolina

ESP32 LX6 procesor, inače srce VIDI Project X-a i proizvod tvrtke Espressif Systems, podržava višejezgreni rad i to u malom pakiranju.

Vidi X u višejezgrenoj okolini

Višejezgreni procesori nisu nikakva novost, odnosno već 20 godina postoje na komercijalnom tržištu. Različite mikroprocesorske arhitekture prihvatile su višejezgrenost kao standard u računalstvu koje ipak doseže svoje krajnje granice.

Xtensa LX6 procesor našeg X-a je low cost i low power procesor kojeg “krasi” i višejezgrenost. Kako “zaposliti” obje jezgre na primjeru našeg mini projekta, pročitajte u nastavku…

 

Malo o Moorevom zakonu i više jezgri

Prema originalnom Mooreovom zakonu iz 1975. godine, broj tranzistora na jednom čipu udvostručuje se svake godine. Davno je bila ta 1975., a Mooreov zakon prošao je kroz nekoliko “nadogradnji”. Bez obzira na tako dug period postojanja, Mooreov zakon neće vrijediti beskonačno jer će tehnologija u jednom trenutku dosegnuti granicu fizičkih zakona.

I sam je Moore vjerovao da će Zakon trajati 30 godina, ali je u stvarnosti to trajalo puno duže i tako bi trebalo biti sve do skorašnje 2025. godine. Međutim, i tu su razna špekuliranja o tome da će se “produžiti trajanje”, kao što je do bilo i ranijih godina jer broj tranzistora po procesoru i dalje eksponencijalno raste.

Međutim, kada se priča o radnom taktu procesora, on je prestao rasti još 2005. godine, a kao razlog navodi se povećanje potrošnje struje, a time i veće zagrijavanje.

Proizvođači su se zato okrenuli drugačijoj priči u povećanju performansi procesora te umjesto povećanja brzine, povećali su broj CPU-a na jednom mikroprocesorskom čipu, odnosno nastali su višejezgreni procesori.

Prvi komercijalni višejezgreni procesor izašao je iz IBM-ovih laboratorija i to pod nazivom IBM POWER4 davne 2001. godine. On je s dvije 64-bitne procesorske jezgre radio na 1.1 GHz ili 1.3 GHz, a sadržavao je 174 milijuna tranzistora.

No, tu pričamo o dva potpuno različita pristupa u povećanju brzine: dok smo kod povećanjem takta procesora dobivali linearno povećanje brzine izvršavanja programa, kod koncepta višejezgrenih procesora nužna je prilagodba u pisanju programa da bi isti iskoristio više jezgri.

 

Malo o projektu

Iako ne spade u kategoriju POWER4 CPU-u, prema tehničkom opisu Xtensa ESP32 LX6 procesor, inače srce VIDI X projekta i proizvod tvrtke Espressif Systems, podržava višejezgreni rad i to u malom pakiranju.

 

Neke osnovne značajke ESP32 Xtensa LX6 mikroprocesora:

■ CPU: Espressif Systems Xtensa dual-core (or single-core) 32-bit LX6 mikroprocesor, na taktu od 160 ili 240 MHz @ up to 600 DMIPS

■ Ultra low power (ULP) co-processor

■ Memory: 520 KB SRAM

 

Ideja projekta je demonstrirati višejezgreni rad tako da svaka pojedina jezgra procesora preuzme izvršavanje našeg testnog koda (programa). Jedan program palit će plavu lampicu na našem X-u, a drugi program također će paliti lampicu, samo uz nešto kraći delay. Izvršavanje programa pratit će obavijest na LCD-u o trenutno aktivnom procesu i jezgri koja ga izvršava (0 ili 1, odnosno prva ili druga jezgra).

Funkcija koju ćemo koristiti za detekciju jezgre koja izvršava program je xPortGetCoreID()

Sljedeće što moramo učiniti je kreirati zadatak (Task) za svaki proces koji će se izvršavati. Naš prvi proces nosit će naziv Task1 i palit će plavu lampicu na X-u, a drugi proces naziv Task2 također će paliti lampicu, samo uz kraći delay. To činimo pomoću naredbe TaskHandle_t naziv_zadatka.

 

TaskHandle_t Task1; // definiramo zadatak
TaskHandle_t Task2; // definiramo zadatak

// pinovi LED
const int led1 = 2; // definiramo pin spojene LED-ice
const int led2 = 2; // definiramo pin spojene LED-ice

 

Zatim je potrebno “pridružiti” kreirane zadatke (Tasks) koji će se izvršavati na svakoj pojedinoj CPU jezgri. Slijedi funkcija xTaskCreatePinnedToCore koja ima nekoliko važnih argumenata i to:

Task1code, /* Funkcija koja će implementirati zadatak */
      "Task1", /* Ime prethodno kreiranog zadatka */
      10000,  /* veličina stacka u riječima */
      NULL,  /* Task input parameter */
      0,  /* Prioritet zadatka */
      &Task1,  /* Upravljanje zadatkom */
      0); /* Procesorska jezgra gdje će se zadatak izvršavati */

 

 

Nakon stvaranja zadataka-taskova, potrebno je stvoriti funkcije koje će izvršavati te zadatke. Obratite pozornost na korištenje funkcije void * što znači da funkcija može preuzeti pokazivač (pointer) koji ne mora biti specifičnog tipa.

void Task1code( void * pvParameters ){
  Serial.print("Zadatak 1 se izvršava na jezgri");
  Serial.println(xPortGetCoreID()); // kreira zadatak koji će biti izvršen u Task1code i Task2code funkciji

  for(;;){
    digitalWrite(led1, HIGH);
    delay(1000);
    digitalWrite(led1, LOW);
    delay(1000);
  }
}

 

 

Kompletan kod potražite na: https://github.com/VidiLAB-com/Vidi-X/tree/master/Dual_core

 

 

#include <gfxfont.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>

/* Display */
#define TFT_DC 21
#define TFT_CS 5

/* color definition */
#define WHITE  0xFFFF
#define BLACK  0x0000

/* Display library*/
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC);

TaskHandle_t Task1;
TaskHandle_t Task2;

// LED pins
const int led1 = 2;
const int led2 = 2; // moguće zamjeniti drugom LED-icom na drugom PIN-u

Učitali smo biblioteke za TFT prikaz kao što to činimo u većini x projekata da bismo status mogli pratiti na TFT ekranu (Adafruit_ILI9341.h i Adafruit_GFX.h). Naredbom TaskHandle_t definiramo taskove (zadatke) koje želimo izvršavati. U našem slučaju Task1 i Task2.

 

void setup() {
tft.begin();
tft.setRotation(3);
tft.fillScreen(BLACK);
Serial.begin(115200);
pinMode(led1, OUTPUT);
pinMode(led2, OUTPUT);

  //kreira zadatak koji će biti izvršen u Task1code() funkciji, sa prioritetom 1 i izvršenjem na jezgri 0
  xTaskCreatePinnedToCore(
                    Task1code,   
                    "Task1",     
                    10000,       
                    NULL,        
                    1,           
                    &Task1,      
                    0);                           
  delay(500); 

  //kreira zadatak koji će biti izvršen u Task2code() funkciji, sa prioritetom 1 i izvršenjem na jezgri 1
  xTaskCreatePinnedToCore(
                    Task2code,   
                    "Task2",     
                    10000,       
                    NULL,        
                    1,           
                    &Task2,      
                    1);          
    delay(500); 
}

 

Naredba xTaskCreatePinnedToCore kreira zadatak koji će biti izvršen u Task1code i Task2code funkciji, s prioritetom 1 i izvršenjem na jezgri 0, odnosno s prioritetom 1 i izvršenjem na jezgri 1. (prva i druga jezgra).Task1 palit će našu LED-icu uz delay od 1000 ms. Task2 to će činiti isto, samo uz nešto kraći delay od 700 ms.

 

 

Paljenjem plave lampice izvršava se kreirani zadatak. Kreirane zadatke izvršavaju pojedine jezgre prema prioritetima.